From 4e4aae1a9f8bbe3f185b5339d9047e2b81c5585a Mon Sep 17 00:00:00 2001 From: Matteo Paonessa Date: Sat, 3 Jun 2017 12:22:23 +0200 Subject: [PATCH] Initial --- CMakeLists.txt | 4 ++-- README.md | 6 +++-- caesium/caesium.c | 17 ++++++++++---- caesium/caesium.h | 4 +++- caesium/error.c | 8 +++++++ caesium/jpeg.c | 56 ++++++++++++++++++++++++++++++----------------- caesium/jpeg.h | 4 ++-- caesium/png.c | 10 ++++++--- demo/main.c | 2 +- install.sh | 4 ++-- 10 files changed, 78 insertions(+), 37 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bd85c4..d5ce4ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,8 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # The version number. set(VERSION_MAJOR 0) -set(VERSION_MINOR 2) -set(VERSION_PATCH 2) +set(VERSION_MINOR 3) +set(VERSION_PATCH 0) configure_file( "caesium/config.h.in" diff --git a/README.md b/README.md index 7ae933a..3dc6ed7 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Binaries not available yet. Please refer to the compilation section below. Libcaesium exposes one single function to compress, auto-detecting the input file type: ```C bool cs_compress(const char *input, - const char *output, + char *output, cs_image_pars *options); ``` #### Parameters @@ -41,13 +41,15 @@ typedef struct cs_jpeg_pars int quality; bool exif_copy; int dct_method; + bool progressive; } cs_jpeg_pars; ``` The first 3 parameters matters, in term of compression, while the others will be set by the compressor/decompressor during the compression progress and thus they will be overwritten. - **quality**: in a range from 0 to 100, the quality of the resulting image. **Note** that 0 means _optimization_ (see below). Default: 0. -- **exif_copy**: set it to _true_ to copy EXIF tag info after compression. Default: false; +- **exif_copy**: set it to _true_ to copy EXIF tag info after compression. Default: false. - **dct_method**: one of the turbojpeg DCT flags. Default: TJFLAG_FASTDCT. +- **progressive**: set to _true_ to output a progressive JPEG, _false_ for scanline. Default: false. ### PNG ```C diff --git a/caesium/caesium.c b/caesium/caesium.c index c69d4c1..6b1a0ab 100644 --- a/caesium/caesium.c +++ b/caesium/caesium.c @@ -6,7 +6,7 @@ #include "png.h" #include "jpeg.h" -bool cs_compress(const char *input_path, const char *output_path, cs_image_pars *options) +bool cs_compress(const char *input_path, char *output_path, cs_image_pars *options) { FILE *pInputFile; image_type type; @@ -21,15 +21,23 @@ bool cs_compress(const char *input_path, const char *output_path, cs_image_pars fclose(pInputFile); + //Same input and output, we need to use a temporary file + if (strcmp(input_path, output_path) == 0) { + output_path = realloc(output_path, (strlen(output_path) + 4) * sizeof(char)); + snprintf(output_path, strlen(output_path) + 4, "%s.cs", output_path); + } + if (type == UNKN) { display_error(WARNING, 103); } else if (type == CS_JPEG) { if (options->jpeg.quality != 0) { - cs_jpeg_compress(output_path, cs_jpeg_decompress(input_path, &options->jpeg), &options->jpeg); + result = cs_jpeg_compress(output_path, cs_jpeg_decompress(input_path, &options->jpeg), &options->jpeg); //The output is now the new input for optimization - result = cs_jpeg_optimize(output_path, output_path, options->jpeg.exif_copy, input_path); + if (result) { + result = cs_jpeg_optimize(output_path, output_path, &options->jpeg, input_path); + } } else { - result = cs_jpeg_optimize(input_path, output_path, options->jpeg.exif_copy, input_path); + result = cs_jpeg_optimize(input_path, output_path, &options->jpeg, input_path); } } else if (type == CS_PNG) { result = cs_png_optimize(input_path, output_path, &options->png); @@ -43,6 +51,7 @@ void initialize_jpeg_parameters(cs_image_pars *options) options->jpeg.quality = 0; options->jpeg.exif_copy = false; options->jpeg.dct_method = 2048; + options->jpeg.progressive = false; } void initialize_png_parameters(cs_image_pars *par) diff --git a/caesium/caesium.h b/caesium/caesium.h index cba71c1..79c3884 100644 --- a/caesium/caesium.h +++ b/caesium/caesium.h @@ -6,12 +6,14 @@ extern "C" { #endif #include +#include typedef struct cs_jpeg_pars { int quality; bool exif_copy; int dct_method; + bool progressive; /* * Parameters you have no reason to set as they will be * overwritten during the process @@ -51,7 +53,7 @@ typedef enum error_level WARNING = 1 } error_level; -bool cs_compress(const char *input_path, const char *output_path, cs_image_pars *options); +bool cs_compress(const char *input_path, char *output_path, cs_image_pars *options); cs_image_pars initialize_parameters(); #ifdef __cplusplus diff --git a/caesium/error.c b/caesium/error.c index 35b0683..96057b0 100644 --- a/caesium/error.c +++ b/caesium/error.c @@ -33,6 +33,14 @@ const char *get_error_message(int code) return "Failed to open JPEG file while compressing"; case 204: return "Failed to open JPEG file while decompressing"; + case 205: + return "Failed to retrieve input JPEG file size"; + case 206: + return "Input JPEG file is too big"; + case 207: + return "Compressor failed"; + case 208: + return "Compressor failed"; //PNG related errors case 300: diff --git a/caesium/jpeg.c b/caesium/jpeg.c index 3d41356..b2866b9 100644 --- a/caesium/jpeg.c +++ b/caesium/jpeg.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "jpeg.h" #include "error.h" @@ -28,14 +29,14 @@ struct jpeg_decompress_struct cs_get_markers(const char *input) jpeg_save_markers(&einfo, JPEG_APP0 + m, 0xFFFF); } - jpeg_read_header(&einfo, TRUE); + jpeg_read_header(&einfo, true); fclose(fp); return einfo; } -bool cs_jpeg_optimize(const char *input_file, const char *output_file, bool exif, const char *exif_src) +bool cs_jpeg_optimize(const char *input_file, const char *output_file, cs_jpeg_pars *options, const char *exif_src) { //File pointer for both input and output FILE *fp; @@ -66,14 +67,14 @@ bool cs_jpeg_optimize(const char *input_file, const char *output_file, bool exif jpeg_stdio_src(&srcinfo, fp); //Save EXIF info - if (exif) { + if (options->exif_copy) { for (int m = 0; m < 16; m++) { jpeg_save_markers(&srcinfo, JPEG_APP0 + m, 0xFFFF); } } //Read the input headers - (void) jpeg_read_header(&srcinfo, TRUE); + (void) jpeg_read_header(&srcinfo, true); //Read input coefficents @@ -94,9 +95,12 @@ bool cs_jpeg_optimize(const char *input_file, const char *output_file, bool exif } //CRITICAL - This is the optimization step - dstinfo.optimize_coding = TRUE; + dstinfo.optimize_coding = true; + //TODO //Progressive - jpeg_simple_progression(&dstinfo); + if (options->progressive) { + jpeg_simple_progression(&dstinfo); + } //Set the output file parameters jpeg_stdio_dest(&dstinfo, fp); @@ -105,7 +109,7 @@ bool cs_jpeg_optimize(const char *input_file, const char *output_file, bool exif jpeg_write_coefficients(&dstinfo, dst_coef_arrays); //Write EXIF - if (exif) { + if (options->exif_copy) { if (strcmp(input_file, exif_src) == 0) { jcopy_markers_execute(&srcinfo, &dstinfo); } else { @@ -128,24 +132,23 @@ bool cs_jpeg_optimize(const char *input_file, const char *output_file, bool exif return true; } -void cs_jpeg_compress(const char *output_file, unsigned char *image_buffer, cs_jpeg_pars *options) +bool cs_jpeg_compress(const char *output_file, unsigned char *image_buffer, cs_jpeg_pars *options) { FILE *fp; tjhandle tjCompressHandle; unsigned char *output_buffer; unsigned long output_size = 0; - + output_buffer = NULL; + int result = 0; //Check for errors if ((fp = fopen(output_file, "wb")) == NULL) { display_error(ERROR, 203); } - output_buffer = NULL; tjCompressHandle = tjInitCompress(); - //TODO Error checks - tjCompress2(tjCompressHandle, + result = tjCompress2(tjCompressHandle, image_buffer, options->width, 0, @@ -156,35 +159,45 @@ void cs_jpeg_compress(const char *output_file, unsigned char *image_buffer, cs_j options->subsample, options->quality, options->dct_method); - - fwrite(output_buffer, output_size, 1, fp); + if (result == -1) { + display_error(ERROR, 207); + } else { + fwrite(output_buffer, output_size, 1, fp); + } fclose(fp); tjDestroy(tjCompressHandle); tjFree(output_buffer); tjFree(image_buffer); + + return true; } unsigned char *cs_jpeg_decompress(const char *fileName, cs_jpeg_pars *options) { - //TODO This has a lot of non checked errors that may occur FILE *fp; long sourceJpegBufferSize = 0; unsigned char *sourceJpegBuffer = NULL; tjhandle tjDecompressHandle; - int fileWidth = 0, fileHeight = 0, jpegSubsamp = 0, colorSpace = 0; + int fileWidth = 0, fileHeight = 0, jpegSubsamp = 0, colorSpace = 0, result = 0; if ((fp = fopen(fileName, "rb")) == NULL) { display_error(ERROR, 204); } fseek(fp, 0, SEEK_END); sourceJpegBufferSize = ftell(fp); - sourceJpegBuffer = tjAlloc(sourceJpegBufferSize); + if (sourceJpegBufferSize == -1) { + display_error(ERROR, 205); + } + if (sourceJpegBufferSize > INT_MAX) { + display_error(ERROR, 206); + } + sourceJpegBuffer = tjAlloc((int) sourceJpegBufferSize); fseek(fp, 0, SEEK_SET); fread(sourceJpegBuffer, (size_t) sourceJpegBufferSize, 1, fp); tjDecompressHandle = tjInitDecompress(); - tjDecompressHeader3(tjDecompressHandle, sourceJpegBuffer, sourceJpegBufferSize, &fileWidth, &fileHeight, + tjDecompressHeader3(tjDecompressHandle, sourceJpegBuffer, (unsigned long) sourceJpegBufferSize, &fileWidth, &fileHeight, &jpegSubsamp, &colorSpace); options->width = fileWidth; @@ -195,15 +208,18 @@ unsigned char *cs_jpeg_decompress(const char *fileName, cs_jpeg_pars *options) unsigned char *temp = tjAlloc(options->width * options->height * tjPixelSize[options->color_space]); - tjDecompress2(tjDecompressHandle, + result = tjDecompress2(tjDecompressHandle, sourceJpegBuffer, - sourceJpegBufferSize, + (unsigned long) sourceJpegBufferSize, temp, options->width, 0, options->height, options->color_space, options->dct_method); + if (result == -1) { + display_error(ERROR, 208); + } tjDestroy(tjDecompressHandle); tjFree(sourceJpegBuffer); diff --git a/caesium/jpeg.h b/caesium/jpeg.h index 2e77be5..2d7d97f 100644 --- a/caesium/jpeg.h +++ b/caesium/jpeg.h @@ -5,11 +5,11 @@ #include "caesium.h" -bool cs_jpeg_optimize(const char *input_file, const char *output_file, bool exif, const char *exif_src); +bool cs_jpeg_optimize(const char *input_file, const char *output_file, cs_jpeg_pars *options, const char *exif_src); struct jpeg_decompress_struct cs_get_markers(const char *input); -void cs_jpeg_compress(const char *output_file, unsigned char *image_buffer, cs_jpeg_pars *options); +bool cs_jpeg_compress(const char *output_file, unsigned char *image_buffer, cs_jpeg_pars *options); unsigned char *cs_jpeg_decompress(const char *fileName, cs_jpeg_pars *options); diff --git a/caesium/png.c b/caesium/png.c index 32df8d5..c5a0e17 100644 --- a/caesium/png.c +++ b/caesium/png.c @@ -13,6 +13,7 @@ bool cs_png_optimize(const char *input, const char *output, cs_png_pars *options { bool result = false; CZopfliPNGOptions png_options; + int error_code = 0; CZopfliPNGSetDefaults(&png_options); @@ -32,7 +33,7 @@ bool cs_png_optimize(const char *input, const char *output, cs_png_pars *options png_options.auto_filter_strategy = options->auto_filter_strategy; if (lodepng_load_file(&orig_buffer, &orig_buffer_size, input) != 0) { - display_error(ERROR, 300); + error_code = 300; goto cleanup; } @@ -42,12 +43,12 @@ bool cs_png_optimize(const char *input, const char *output, cs_png_pars *options 0, &resultpng, &resultpng_size) != 0) { - display_error(ERROR, 301); + error_code = 301; goto cleanup; } if (lodepng_save_file(resultpng, resultpng_size, output) != 0) { - display_error(ERROR, 302); + error_code = 302; goto cleanup; } @@ -56,5 +57,8 @@ bool cs_png_optimize(const char *input, const char *output, cs_png_pars *options cleanup: free(orig_buffer); free(resultpng); + if (error_code != 0) { + display_error(ERROR, error_code); + } return result; } diff --git a/demo/main.c b/demo/main.c index 1cdf325..b5f337a 100644 --- a/demo/main.c +++ b/demo/main.c @@ -5,7 +5,7 @@ int main(int argc, char *argv[]) { - fprintf(stdout, "libcaesium demo application v%d.%d.%d\n\n", + fprintf(stdout, "libcaesium demo application v%d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); diff --git a/install.sh b/install.sh index b4d6751..c3ace3d 100755 --- a/install.sh +++ b/install.sh @@ -11,7 +11,7 @@ mkdir build && cd build ../configure make && sudo make install -cd $SOURCE +cd ${SOURCE} #zopflipng git clone https://github.com/google/zopfli.git @@ -23,4 +23,4 @@ sudo ln -s libzopflipng.so.1.0.0 /usr/lib/libzopflipng.so.1 sudo mkdir /usr/include/zopflipng sudo cp src/zopflipng/zopflipng_lib.h /usr/include/zopflipng -cd $SOURCE +cd ${SOURCE}