Skip to content

Instantly share code, notes, and snippets.

@dcommander
Last active August 28, 2024 17:41
Show Gist options
  • Save dcommander/80a46f4e4c96df8b0cd42673275aa7f1 to your computer and use it in GitHub Desktop.
Save dcommander/80a46f4e4c96df8b0cd42673275aa7f1 to your computer and use it in GitHub Desktop.
Program, based on tjexample.c, that demonstrates how to use the TurboJPEG 3 API to capture DCT coefficients from a JPEG file, modify them, and write them to another JPEG file
/*
* Copyright (C)2011-2012, 2014-2015, 2017, 2019, 2021-2024
* D. R. Commander. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the libjpeg-turbo Project nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program, which is based on tjexample.c, demonstrates how to use the
* TurboJPEG 3 API to capture DCT coefficients from a JPEG file, modify them,
* and write them to another JPEG file.
*/
#ifdef _MSC_VER
#define _CRT_SECURE_NO_DEPRECATE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#if !defined(_MSC_VER) || _MSC_VER > 1600
#include <stdint.h>
#endif
#include <turbojpeg.h>
#define THROW(action, message) { \
printf("ERROR in line %d while %s:\n%s\n", __LINE__, action, message); \
retval = -1; goto bailout; \
}
#define THROW_TJ(action) THROW(action, tj3GetErrorStr(tjInstance))
#define THROW_UNIX(action) THROW(action, strerror(errno))
static int customInputFilter(short *coeffs, tjregion arrayRegion,
tjregion planeRegion, int componentIndex,
int transformIndex, tjtransform *transform)
{
short **dctPlane = (short **)transform->data;
if (dctPlane && &dctPlane[componentIndex])
memcpy(
&dctPlane[componentIndex][arrayRegion.y * planeRegion.w + arrayRegion.x],
coeffs, arrayRegion.w * arrayRegion.h * sizeof(short));
return 0;
}
static int customOutputFilter(short *coeffs, tjregion arrayRegion,
tjregion planeRegion, int componentIndex,
int transformIndex, tjtransform *transform)
{
short **dctPlane = (short **)transform->data;
if (dctPlane && &dctPlane[componentIndex])
memcpy(coeffs,
&dctPlane[componentIndex][arrayRegion.y * planeRegion.w + arrayRegion.x],
arrayRegion.w * arrayRegion.h * sizeof(short));
return 0;
}
int main(int argc, char **argv)
{
int i, retval = 0;
FILE *jpegFile = NULL;
unsigned char *jpegBuf = NULL, *dstBuf = NULL;
size_t jpegSize, dstSize = 0, dctBufSize = 0;
long size;
tjtransform xform;
tjhandle tjInstance = NULL;
int width, height, colorspace, subsamp;
short *dctBuf = NULL;
short *dctPlane[4] = { NULL, NULL, NULL, NULL };
memset(&xform, 0, sizeof(tjtransform));
if (argc < 3) {
printf("\nUSAGE: %s <Input image> <Output image>\n\n", argv[0]);
exit(1);
}
if ((tjInstance = tj3Init(TJINIT_TRANSFORM)) == NULL)
THROW_TJ("creating TurboJPEG instance");
/* Read input image from disk into memory */
if ((jpegFile = fopen(argv[1], "rb")) == NULL)
THROW_UNIX("opening input file");
if (fseek(jpegFile, 0, SEEK_END) < 0 || ((size = ftell(jpegFile)) < 0) ||
fseek(jpegFile, 0, SEEK_SET) < 0)
THROW_UNIX("determining input file size");
if (size == 0)
THROW("determining input file size", "Input file contains no data");
jpegSize = size;
if ((jpegBuf = tj3Alloc(jpegSize)) == NULL)
THROW_UNIX("allocating JPEG buffer");
if (fread(jpegBuf, jpegSize, 1, jpegFile) < 1)
THROW_UNIX("reading input file");
fclose(jpegFile); jpegFile = NULL;
/* Read JPEG header */
if (tj3DecompressHeader(tjInstance, jpegBuf, jpegSize) < 0)
THROW_TJ("reading JPEG header");
width = tj3Get(tjInstance, TJPARAM_JPEGWIDTH);
height = tj3Get(tjInstance, TJPARAM_JPEGHEIGHT);
colorspace = tj3Get(tjInstance, TJPARAM_COLORSPACE);
if (colorspace == TJCS_YCCK || colorspace == TJCS_CMYK)
THROW("reading JPEG header", "YCCK and CMYK JPEG images are not supported");
if ((subsamp = tj3Get(tjInstance, TJPARAM_SUBSAMP)) < 0)
THROW_TJ("determining the chrominance subsampling level of the JPEG image");
/* Allocate DCT buffer */
dctBufSize += tj3YUVPlaneSize(0, width, 0, height, subsamp);
if (colorspace != TJCS_GRAY) {
dctBufSize += tj3YUVPlaneSize(1, width, 0, height, subsamp);
dctBufSize += tj3YUVPlaneSize(2, width, 0, height, subsamp);
}
if ((dctBuf = (short *)malloc(dctBufSize * sizeof(short))) == NULL)
THROW_UNIX("allocating DCT buffer");
dctPlane[0] = dctBuf;
if (colorspace != TJCS_GRAY) {
dctPlane[1] = &dctPlane[0][tj3YUVPlaneSize(0, width, 0, height, subsamp)];
dctPlane[2] = &dctPlane[1][tj3YUVPlaneSize(1, width, 0, height, subsamp)];
}
/* First pass: copy DCT coefficients from input image into DCT buffer */
xform.options = TJXOPT_NOOUTPUT;
xform.customFilter = customInputFilter;
xform.data = (void *)&dctPlane;
if (tj3Transform(tjInstance, jpegBuf, jpegSize, 1, &dstBuf, &dstSize,
&xform) < 0)
THROW_TJ("transforming input image");
/* Negate all DCT coefficients (photo negative)
NOTE: DCT coefficient capture isn't necessary for such a simple filter.
We could apply this filter in a custom filter function using only one
pass. DCT coefficient capture is, however, necessary if you want to move
coefficients around. */
for (i = 0; i < dctBufSize; i++)
dctBuf[i] = -dctBuf[i];
/* Second pass: copy DCT coefficients from DCT buffer into output image
NOTE: There is unnecessary overhead here, because tj3Transform() re-reads
the input image header and performs entropy decoding on the input image
again, even though our filter will replace the decoded coefficients. */
xform.options = 0;
xform.customFilter = customOutputFilter;
if (tj3Transform(tjInstance, jpegBuf, jpegSize, 1, &dstBuf, &dstSize,
&xform) < 0)
THROW_TJ("transforming input image");
if ((jpegFile = fopen(argv[2], "wb")) == NULL)
THROW_UNIX("opening output file");
if (fwrite(dstBuf, dstSize, 1, jpegFile) < 1)
THROW_UNIX("writing output file");
bailout:
tj3Destroy(tjInstance);
if (jpegFile) fclose(jpegFile);
tj3Free(jpegBuf);
free(dctBuf);
tj3Free(dstBuf);
return retval;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment