libcaesium/caesium/jpeg.c

263 lines
6.4 KiB
C
Raw Normal View History

2016-11-13 14:43:54 +01:00
#include <stdio.h>
#include <jpeglib.h>
#include <string.h>
#include <turbojpeg.h>
2017-06-03 12:22:23 +02:00
#include <limits.h>
2017-12-29 23:09:13 +01:00
#include <math.h>
2016-11-13 14:43:54 +01:00
#include "jpeg.h"
2016-11-14 18:56:24 +01:00
#include "error.h"
2016-11-13 14:43:54 +01:00
struct jpeg_decompress_struct cs_get_markers(const char *input)
{
FILE *fp;
struct jpeg_decompress_struct einfo;
struct jpeg_error_mgr eerr;
einfo.err = jpeg_std_error(&eerr);
jpeg_create_decompress(&einfo);
//Check for errors
2017-03-19 20:58:28 +01:00
if ((fp = fopen(input, "rb")) == NULL) {
2017-02-21 23:15:31 +01:00
display_error(ERROR, 200);
2016-11-13 14:43:54 +01:00
}
//Create the IO instance for the input file
jpeg_stdio_src(&einfo, fp);
//Save EXIF info
2018-11-25 15:54:20 +01:00
jpeg_save_markers(&einfo, JPEG_COM, 0xFFFF);
2016-11-13 14:43:54 +01:00
for (int m = 0; m < 16; m++) {
jpeg_save_markers(&einfo, JPEG_APP0 + m, 0xFFFF);
}
2017-06-03 12:22:23 +02:00
jpeg_read_header(&einfo, true);
2016-11-13 14:43:54 +01:00
fclose(fp);
return einfo;
}
2017-06-03 12:22:23 +02:00
bool cs_jpeg_optimize(const char *input_file, const char *output_file, cs_jpeg_pars *options, const char *exif_src)
2016-11-13 14:43:54 +01:00
{
//File pointer for both input and output
FILE *fp;
//Those will hold the input/output structs
struct jpeg_decompress_struct srcinfo;
struct jpeg_compress_struct dstinfo;
//Error handling
struct jpeg_error_mgr jsrcerr, jdsterr;
//Input/Output array coefficents
jvirt_barray_ptr *src_coef_arrays;
jvirt_barray_ptr *dst_coef_arrays;
//Set errors and create the compress/decompress istances
srcinfo.err = jpeg_std_error(&jsrcerr);
jpeg_create_decompress(&srcinfo);
dstinfo.err = jpeg_std_error(&jdsterr);
jpeg_create_compress(&dstinfo);
//Check for errors
2017-03-19 20:58:28 +01:00
if ((fp = fopen(input_file, "rb")) == NULL) {
2017-02-21 23:15:31 +01:00
display_error(ERROR, 201);
2016-11-13 14:43:54 +01:00
}
2016-11-14 18:56:24 +01:00
//Create the IO instance for the input file
2016-11-13 14:43:54 +01:00
jpeg_stdio_src(&srcinfo, fp);
//Save EXIF info
2017-06-03 12:22:23 +02:00
if (options->exif_copy) {
2018-11-25 15:54:20 +01:00
jpeg_save_markers(&srcinfo, JPEG_COM, 0xFFFF);
2016-11-13 14:43:54 +01:00
for (int m = 0; m < 16; m++) {
jpeg_save_markers(&srcinfo, JPEG_APP0 + m, 0xFFFF);
}
}
//Read the input headers
2017-06-03 12:22:23 +02:00
(void) jpeg_read_header(&srcinfo, true);
2016-11-13 14:43:54 +01:00
//Read input coefficents
src_coef_arrays = jpeg_read_coefficients(&srcinfo);
//Copy parameters
jpeg_copy_critical_parameters(&srcinfo, &dstinfo);
//Set coefficents array to be the same
dst_coef_arrays = src_coef_arrays;
//We don't need the input file anymore
fclose(fp);
//Check for errors
2016-11-14 18:56:24 +01:00
if ((fp = fopen(output_file, "wb")) == NULL) {
2017-02-21 23:15:31 +01:00
display_error(ERROR, 202);
2016-11-13 14:43:54 +01:00
}
//CRITICAL - This is the optimization step
2017-06-03 12:22:23 +02:00
dstinfo.optimize_coding = true;
2016-11-13 14:43:54 +01:00
//Set the output file parameters
jpeg_stdio_dest(&dstinfo, fp);
2016-11-14 18:56:24 +01:00
//Actually write the coefficients
2016-11-13 14:43:54 +01:00
jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
//Write EXIF
2017-06-03 12:22:23 +02:00
if (options->exif_copy) {
2016-11-13 14:43:54 +01:00
if (strcmp(input_file, exif_src) == 0) {
jcopy_markers_execute(&srcinfo, &dstinfo);
} else {
//For standard compression EXIF data
struct jpeg_decompress_struct einfo = cs_get_markers(exif_src);
jcopy_markers_execute(&einfo, &dstinfo);
jpeg_destroy_decompress(&einfo);
}
}
//Finish and free
jpeg_finish_compress(&dstinfo);
jpeg_destroy_compress(&dstinfo);
(void) jpeg_finish_decompress(&srcinfo);
jpeg_destroy_decompress(&srcinfo);
//Close the output file
fclose(fp);
2016-12-23 09:10:21 +01:00
return true;
2016-11-13 14:43:54 +01:00
}
2017-12-29 23:09:13 +01:00
int cs_jpeg_compress(const char *output_file, unsigned char *image_buffer, cs_jpeg_pars *options)
2016-11-13 14:43:54 +01:00
{
2017-12-29 23:09:13 +01:00
if (image_buffer == 0) {
return 2;
}
2016-11-13 14:43:54 +01:00
FILE *fp;
tjhandle tjCompressHandle;
unsigned char *output_buffer;
unsigned long output_size = 0;
2017-06-03 12:22:23 +02:00
output_buffer = NULL;
int result = 0;
2016-11-13 14:43:54 +01:00
//Check for errors
2016-11-14 18:56:24 +01:00
if ((fp = fopen(output_file, "wb")) == NULL) {
2017-02-21 23:15:31 +01:00
display_error(ERROR, 203);
2016-11-13 14:43:54 +01:00
}
tjCompressHandle = tjInitCompress();
2017-06-03 12:22:23 +02:00
result = tjCompress2(tjCompressHandle,
2016-11-13 14:43:54 +01:00
image_buffer,
options->width,
0,
options->height,
options->color_space,
&output_buffer,
&output_size,
options->subsample,
options->quality,
options->dct_method);
2017-06-03 12:22:23 +02:00
if (result == -1) {
display_error(ERROR, 207);
} else {
fwrite(output_buffer, output_size, 1, fp);
}
2016-11-13 14:43:54 +01:00
fclose(fp);
tjDestroy(tjCompressHandle);
tjFree(output_buffer);
2017-01-30 15:20:34 +01:00
tjFree(image_buffer);
2017-06-03 12:22:23 +02:00
2017-12-29 23:09:13 +01:00
return 1;
2016-11-13 14:43:54 +01:00
}
unsigned char *cs_jpeg_decompress(const char *fileName, cs_jpeg_pars *options)
{
2016-11-14 18:56:24 +01:00
FILE *fp;
long sourceJpegBufferSize = 0;
2016-11-13 14:43:54 +01:00
unsigned char *sourceJpegBuffer = NULL;
tjhandle tjDecompressHandle;
2017-06-03 12:22:23 +02:00
int fileWidth = 0, fileHeight = 0, jpegSubsamp = 0, colorSpace = 0, result = 0;
2016-11-13 14:43:54 +01:00
2016-11-14 18:56:24 +01:00
if ((fp = fopen(fileName, "rb")) == NULL) {
2017-02-21 23:15:31 +01:00
display_error(ERROR, 204);
2016-11-14 18:56:24 +01:00
}
fseek(fp, 0, SEEK_END);
sourceJpegBufferSize = ftell(fp);
2017-06-03 12:22:23 +02:00
if (sourceJpegBufferSize == -1) {
display_error(ERROR, 205);
}
if (sourceJpegBufferSize > INT_MAX) {
display_error(ERROR, 206);
}
sourceJpegBuffer = tjAlloc((int) sourceJpegBufferSize);
2016-11-13 14:43:54 +01:00
2016-11-14 18:56:24 +01:00
fseek(fp, 0, SEEK_SET);
fread(sourceJpegBuffer, (size_t) sourceJpegBufferSize, 1, fp);
2016-11-13 14:43:54 +01:00
tjDecompressHandle = tjInitDecompress();
2017-06-03 12:22:23 +02:00
tjDecompressHeader3(tjDecompressHandle, sourceJpegBuffer, (unsigned long) sourceJpegBufferSize, &fileWidth, &fileHeight,
2016-11-14 18:56:24 +01:00
&jpegSubsamp, &colorSpace);
2016-11-13 14:43:54 +01:00
2017-12-29 23:09:13 +01:00
if (colorSpace == 4) { //CMYK
display_error(WARNING, 209);
return 0;
}
options->width = (int) round(fileWidth * options->scale_factor);
options->height = (int) round(fileHeight * options->scale_factor);
2016-11-13 14:43:54 +01:00
2016-11-14 18:56:24 +01:00
options->subsample = (enum TJSAMP) jpegSubsamp;
2016-11-13 14:43:54 +01:00
options->color_space = colorSpace;
2017-12-29 23:09:13 +01:00
unsigned char *temp = tjAlloc(options->width * options->height * tjPixelSize[colorSpace]);
2016-11-13 14:43:54 +01:00
2017-06-03 12:22:23 +02:00
result = tjDecompress2(tjDecompressHandle,
2016-11-14 18:56:24 +01:00
sourceJpegBuffer,
2017-06-03 12:22:23 +02:00
(unsigned long) sourceJpegBufferSize,
2016-11-14 18:56:24 +01:00
temp,
options->width,
0,
options->height,
2017-12-29 23:09:13 +01:00
colorSpace,
2016-11-14 18:56:24 +01:00
options->dct_method);
2017-06-03 12:22:23 +02:00
if (result == -1) {
display_error(ERROR, 208);
}
2016-11-13 14:43:54 +01:00
2019-09-28 20:00:28 +02:00
fclose(fp);
2016-11-13 14:43:54 +01:00
tjDestroy(tjDecompressHandle);
2017-01-30 15:20:34 +01:00
tjFree(sourceJpegBuffer);
2016-11-13 14:43:54 +01:00
return temp;
}
void jcopy_markers_execute(j_decompress_ptr srcinfo, j_compress_ptr dstinfo)
{
jpeg_saved_marker_ptr marker;
for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) {
if (dstinfo->write_JFIF_header &&
marker->marker == JPEG_APP0 &&
marker->data_length >= 5 &&
GETJOCTET(marker->data[0]) == 0x4A &&
GETJOCTET(marker->data[1]) == 0x46 &&
GETJOCTET(marker->data[2]) == 0x49 &&
GETJOCTET(marker->data[3]) == 0x46 &&
GETJOCTET(marker->data[4]) == 0)
continue; /* reject duplicate JFIF */
if (dstinfo->write_Adobe_marker &&
marker->marker == JPEG_APP0 + 14 &&
marker->data_length >= 5 &&
GETJOCTET(marker->data[0]) == 0x41 &&
GETJOCTET(marker->data[1]) == 0x64 &&
GETJOCTET(marker->data[2]) == 0x6F &&
GETJOCTET(marker->data[3]) == 0x62 &&
GETJOCTET(marker->data[4]) == 0x65)
continue; /* reject duplicate Adobe */
jpeg_write_marker(dstinfo, marker->marker,
marker->data, marker->data_length);
}
}