From 8c71d0d93b648f17ee3fc1b1088e3e7efe7c8535 Mon Sep 17 00:00:00 2001 From: Matteo Paonessa Date: Mon, 18 May 2015 23:52:56 +0200 Subject: [PATCH] Decompression/Compression routine added --- README.md | 4 +- src/Makefile.am | 4 +- src/Makefile.in | 4 +- src/compress.c | 73 ++++++++++++++++++++++------ src/compress.h | 9 ++-- src/lossless.c | 26 +++++++--- src/lossless.h | 6 +-- src/main.c | 96 ++++++------------------------------ src/utils.c | 126 ++++++++++++++++++++++++++++++++++++++++++++++-- src/utils.h | 10 +++- 10 files changed, 241 insertions(+), 117 deletions(-) diff --git a/README.md b/README.md index cd40b5d..b8307ca 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,9 @@ See INSTALL for more details. ---------- ###### TODO -A lot of things. A more detailed list will come later on. +* Lossy compression +* Lossless scale +* ---------- diff --git a/src/Makefile.am b/src/Makefile.am index 3e1862c..44e684e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ bin_PROGRAMS = caesiumclt caesiumclt_SOURCES = main.c utils.c lossless.c compress.c -caesiumclt_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -ljpeg -std=c99 -caesiumclt_OBJCFLAGS = -D_FILE_OFFSET_BITS=64 -ljpeg \ No newline at end of file +caesiumclt_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -ljpeg -lturbojpeg -std=c99 +caesiumclt_OBJCFLAGS = -D_FILE_OFFSET_BITS=64 -ljpeg -lturbojpeg \ No newline at end of file diff --git a/src/Makefile.in b/src/Makefile.in index 2a9a3bc..6f57ed2 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -253,8 +253,8 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ caesiumclt_SOURCES = main.c utils.c lossless.c compress.c -caesiumclt_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -ljpeg -std=c99 -caesiumclt_OBJCFLAGS = -D_FILE_OFFSET_BITS=64 -ljpeg +caesiumclt_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -ljpeg -lturbojpeg -std=c99 +caesiumclt_OBJCFLAGS = -D_FILE_OFFSET_BITS=64 -ljpeg -lturbojpeg all: all-am .SUFFIXES: diff --git a/src/compress.c b/src/compress.c index 56d4974..5f5c513 100755 --- a/src/compress.c +++ b/src/compress.c @@ -1,11 +1,13 @@ #include #include #include +#include #include #include "compress.h" +#include "utils.h" -void cclt_compress(char* output_file, unsigned char* image_buffer) +void cclt_compress(char* output_file, unsigned char* image_buffer, cclt_compress_parameters* pars) { struct jpeg_compress_struct cinfo; @@ -15,8 +17,8 @@ void cclt_compress(char* output_file, unsigned char* image_buffer) int row_stride; if ((outfile = fopen(output_file, "wb")) == NULL) { - fprintf(stderr, "can't open %s\n", output_file); - exit(1); + fprintf(stderr, "can't open %s\n", output_file); + exit(1); } cinfo.err = jpeg_std_error(&jerr); @@ -24,35 +26,34 @@ void cclt_compress(char* output_file, unsigned char* image_buffer) jpeg_create_compress(&cinfo); jpeg_stdio_dest(&cinfo, outfile); - cinfo.image_width = 80; - cinfo.image_height = 80; + cinfo.image_width = pars->width; + cinfo.image_height = pars->height; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); - cinfo.dct_method = JDCT_FLOAT; + cinfo.dct_method = pars->dct_method; cinfo.optimize_coding = TRUE; - cinfo.smoothing_factor = 50; - jpeg_set_quality(&cinfo, 80, TRUE ); + cinfo.smoothing_factor = pars->smoothing_factor; + jpeg_set_quality(&cinfo, pars->quality, TRUE); jpeg_set_colorspace(&cinfo, JCS_RGB); jpeg_start_compress(&cinfo, TRUE); - //TODO cambia - row_stride = 80 * 3;//image_width * 3; + row_stride = pars->width * 3; while (cinfo.next_scanline < cinfo.image_height) { - printf("%d%%\r", cinfo.next_scanline * 100 / cinfo.image_height); + //printf("%d%%\r", cinfo.next_scanline * 100 / cinfo.image_height); - row_pointer[0] = &image_buffer[cinfo.next_scanline * row_stride]; - (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + row_pointer[0] = &image_buffer[cinfo.next_scanline * row_stride]; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); } - printf("%d%%\n", cinfo.next_scanline * 100 / cinfo.image_height); + //printf("%d%%\n", cinfo.next_scanline * 100 / cinfo.image_height); jpeg_finish_compress(&cinfo); @@ -62,3 +63,47 @@ void cclt_compress(char* output_file, unsigned char* image_buffer) jpeg_destroy_compress(&cinfo); } + +unsigned char* cclt_decompress(char* fileName, cclt_compress_parameters* pars) { + + FILE *file = NULL; + int res = 0; + long int sourceJpegBufferSize = 0; + unsigned char* sourceJpegBuffer = NULL; + tjhandle tjDecompressHandle; + int fileWidth = 0, fileHeight = 0, jpegSubsamp = 0; + + file = fopen(fileName, "rb"); + res = fseek(file, 0, SEEK_END); + sourceJpegBufferSize = ftell(file); + sourceJpegBuffer = tjAlloc(sourceJpegBufferSize); + + res = fseek(file, 0, SEEK_SET); + res = fread(sourceJpegBuffer, (long)sourceJpegBufferSize, 1, file); + tjDecompressHandle = tjInitDecompress(); + res = tjDecompressHeader2(tjDecompressHandle, sourceJpegBuffer, sourceJpegBufferSize, &fileWidth, &fileHeight, &jpegSubsamp); + + int destWidth = fileWidth; + int destHeight = fileHeight; + pars->color_space = jpegSubsamp; + + + + unsigned char* temp = tjAlloc(destHeight * destWidth * tjPixelSize[TJPF_RGB]); + + + res = tjDecompress2(tjDecompressHandle, + sourceJpegBuffer, + sourceJpegBufferSize, + temp, + destWidth, + 0, + destHeight, + TJPF_RGB, + TJFLAG_ACCURATEDCT); + + pars->width = destWidth; + pars->height = destHeight; + + return temp; +} diff --git a/src/compress.h b/src/compress.h index 5ded694..60747fe 100755 --- a/src/compress.h +++ b/src/compress.h @@ -1,6 +1,9 @@ -#ifndef CCTL_COMPRESS -#define CCTL_COMPRESS +#ifndef CCLT_COMPRESS +#define CCLT_COMPRESS -void cclt_compress(char* output_file, unsigned char* image_buffer); +#include "utils.h" + +void cclt_compress(char* output_file, unsigned char* image_buffer, cclt_compress_parameters* pars); +unsigned char* cclt_decompress(char* fileName, cclt_compress_parameters* pars); #endif diff --git a/src/lossless.c b/src/lossless.c index c199ea9..20f0454 100755 --- a/src/lossless.c +++ b/src/lossless.c @@ -4,10 +4,11 @@ #include #include "lossless.h" +#include "utils.h" -int cclt_optimize(char* input_file, char* output_file) { +int cclt_optimize(char* input_file, char* output_file, int exif_flag) { //File pointer for both input and output - FILE* fp; + FILE* fp; //Those will hold the input/output structs struct jpeg_decompress_struct srcinfo; @@ -17,8 +18,8 @@ int cclt_optimize(char* input_file, char* output_file) { struct jpeg_error_mgr jsrcerr, jdsterr; //Input/Output array coefficents - jvirt_barray_ptr * src_coef_arrays; - jvirt_barray_ptr * dst_coef_arrays; + 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); @@ -38,12 +39,20 @@ int cclt_optimize(char* input_file, char* output_file) { //Create the IO istance for the input file jpeg_stdio_src(&srcinfo, fp); + + //Save EXIF info + if (exif_flag == 1) { + 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); //Read input coefficents src_coef_arrays = jpeg_read_coefficients(&srcinfo); + //jcopy_markers_setup(&srcinfo, copyoption); //Copy parameters jpeg_copy_critical_parameters(&srcinfo, &dstinfo); @@ -56,7 +65,7 @@ int cclt_optimize(char* input_file, char* output_file) { //Open the output one instead fp = fopen(output_file, "w+"); - //Check for errors + //Check for errors //TODO Use UNIX error messages if (fp == NULL) { printf("OUTPUT: Failed to open file \"%s\"\n", output_file); @@ -66,13 +75,18 @@ int cclt_optimize(char* input_file, char* output_file) { //CRITICAL - This is the optimization step dstinfo.optimize_coding = TRUE; jpeg_simple_progression(&dstinfo); - //dstinfo.dct_method = JDCT_ISLOW; //Set the output file parameters jpeg_stdio_dest(&dstinfo, fp); + //Actually write the coefficents jpeg_write_coefficients(&dstinfo, dst_coef_arrays); + + //Write EXIF + if (exif_flag == 1) { + jcopy_markers_execute(&srcinfo, &dstinfo); + } //Free memory and finish jpeg_finish_compress(&dstinfo); diff --git a/src/lossless.h b/src/lossless.h index 0915ef0..ec9b7a1 100755 --- a/src/lossless.h +++ b/src/lossless.h @@ -1,6 +1,6 @@ -#ifndef CCTL_LOSSLESS -#define CCTL_LOSSLESS +#ifndef CCLT_LOSSLESS +#define CCLT_LOSSLESS -int cclt_optimize(char* input_file, char* output_file); +int cclt_optimize(char* input_file, char* output_file, int exif_flag); #endif diff --git a/src/main.c b/src/main.c index 413c80a..dc58408 100755 --- a/src/main.c +++ b/src/main.c @@ -12,88 +12,19 @@ #include "compress.h" #include "utils.h" -#define APP_VERSION "1.9.9 BETA" -#define BUILD 20150515 - /* PARAMETERS: -q quality - -e exif - -o output folder - -v version - -l lossless - -s scale - -h help + -e exif v + -o output folder v + -v version v + -l lossless v + -s scale + -h help v -R recursive */ //TODO Use a general fuction to support folder separators -cclt_compress_parameters parse_arguments(int argc, char* argv[]) { - - //Initialize default params - cclt_compress_parameters parameters = initialize_compression_parameters(); - int c; - - while (optind < argc) { - if ((c = getopt (argc, argv, "q:velo:s:hR")) != -1) { - switch (c) { - case 'v': - printf("CCLT - Caesium Command Line Tools - Version %s (Build: %d)\n", APP_VERSION, BUILD); - exit(0); - break; - case '?': - if (optopt == 'q' || optopt == 'o' || optopt == 's') { - fprintf (stderr, "Option -%c requires an argument.\n", optopt); - //Arguments without values - exit(-1); - } - else if (isprint(optopt)) { - fprintf (stderr, "Unknown option `-%c'.\n", optopt); - } - else { - fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt); - } - break; - case ':': - fprintf(stderr, "Parameter expected.\n"); - break; - case 'q': - parameters.quality = string_to_int(optarg); - break; - case 'e': - parameters.exif_copy = 1; - break; - case 'l': - parameters.lossless = 1; - break; - case 'o': - parameters.output_folder = optarg; - break; - case 's': - parameters.scaling_factor = string_to_int(optarg); - break; - case 'h': - print_help(); - break; - default: - abort(); - } - } else { - int i = 0; - parameters.input_files = (char**) malloc ((argc - optind) * sizeof (char*)); - while (optind < argc) { - parameters.input_files[i] = (char*) malloc (strlen(argv[optind]) * sizeof(char)); //TODO Necessary?? - parameters.input_files[i] = argv[optind]; - parameters.input_files_count = i + 1; - optind++; - i++; - } - } - } - - return parameters; -} - int main (int argc, char *argv[]) { struct stat st_buf; errno = 0; @@ -138,9 +69,8 @@ int main (int argc, char *argv[]) { fprintf(stderr, "No -o option pointing to the destination folder. Aborting.\n"); exit(-4); } else { - if (mkdir(pars.output_folder, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1) { + if (mkpath(pars.output_folder, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1) { if (errno != EEXIST) { - perror("mkdir"); exit(-5); } } @@ -153,7 +83,7 @@ int main (int argc, char *argv[]) { for (int i = 0; i < pars.input_files_count; i++) { off_t i_size, o_size; int status; //Pointer for stat() call - char* output_filename = (char*) malloc (strlen(pars.output_folder) * sizeof(char)); + char* output_filename = (char*) malloc ((strlen(pars.output_folder) + 2) * sizeof(char)); char* i_tmp = (char*) malloc (strlen(pars.input_files[i]) * sizeof(char)); strcpy(i_tmp, pars.input_files[i]); @@ -163,7 +93,9 @@ int main (int argc, char *argv[]) { if (output_filename[strlen(pars.output_folder - 1)] != '/') { strcat(output_filename, "/"); } - + + output_filename = realloc(output_filename, (strlen(output_filename) + strlen(get_filename_with_extension(i_tmp)) + 1) * sizeof(char)); + output_filename = strcat(output_filename, get_filename_with_extension(i_tmp)); //TODO OVERALL progress update? @@ -183,7 +115,6 @@ int main (int argc, char *argv[]) { } //Get input file size - //On 32bit system, compile with -D_FILE_OFFSET_BITS=64 i_size = st_buf.st_size; i_t_size += i_size; @@ -192,9 +123,12 @@ int main (int argc, char *argv[]) { //Lossless optmization requested if (pars.lossless != 0) { - cclt_optimize(pars.input_files[i], output_filename); + + cclt_optimize(pars.input_files[i], output_filename, pars.exif_copy); } else { //TODO Standard compression requested + unsigned char* buffer = cclt_decompress(pars.input_files[i], &pars); + cclt_compress(output_filename, buffer, &pars); } //Get output stats diff --git a/src/utils.c b/src/utils.c index 6b92c1f..45ae444 100644 --- a/src/utils.c +++ b/src/utils.c @@ -6,6 +6,10 @@ #include #include #include +#include +#include +#include + #include "utils.h" @@ -15,14 +19,15 @@ cclt_compress_parameters initialize_compression_parameters() { par.quality = 0; par.width = 0; par.height = 0; - par.smoothing_factor = 50; + par.smoothing_factor = 100; par.scaling_factor = 100; - //par.color_space = NULL; TODO Must set? - //par.dct_method = NULL; TODO Must set? + par.color_space = JCS_RGB; + par.dct_method = JDCT_ISLOW; par.output_folder = NULL; par.exif_copy = 0; par.lossless = 0; par.input_files_count = 0; + return par; } @@ -73,9 +78,124 @@ void print_progress(int current, int max, char* message) { } } +//TODO Recheck +int mkpath(const char *pathname, mode_t mode) { + + char parent[PATH_MAX], *p; + /* make a parent directory path */ + strncpy(parent, pathname, sizeof(parent)); + parent[sizeof(parent) - 1] = '\0'; + for(p = parent + strlen(parent); *p != '/' && p != parent; p--); + *p = '\0'; + /* try make parent directory */ + if(p != parent && mkpath(parent, mode) != 0) + return -1; + /* make this one if parent has been made */ + if(mkdir(pathname, mode) == 0) + return 0; + /* if it already exists that is fine */ + if(errno == EEXIST) + return 0; + return -1; +} + char* get_filename_with_extension(char* full_path) { char* dest; dest = strrchr(full_path, '/') + 1; + return dest; } + +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; + 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; + jpeg_write_marker(dstinfo, marker->marker, + marker->data, marker->data_length); + } +} + +cclt_compress_parameters parse_arguments(int argc, char* argv[]) { + + //Initialize default params + cclt_compress_parameters parameters = initialize_compression_parameters(); + int c; + + while (optind < argc) { + if ((c = getopt (argc, argv, "q:velo:s:hR")) != -1) { + switch (c) { + case 'v': + printf("CCLT - Caesium Command Line Tools - Version %s (Build: %d)\n", APP_VERSION, BUILD); + exit(0); + break; + case '?': + if (optopt == 'q' || optopt == 'o' || optopt == 's') { + fprintf (stderr, "Option -%c requires an argument.\n", optopt); + //Arguments without values + exit(-1); + } + else if (isprint(optopt)) { + fprintf (stderr, "Unknown option `-%c'.\n", optopt); + } + else { + fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt); + } + break; + case ':': + fprintf(stderr, "Parameter expected.\n"); + break; + case 'q': + parameters.quality = string_to_int(optarg); + break; + case 'e': + parameters.exif_copy = 1; + break; + case 'l': + parameters.lossless = 1; + break; + case 'o': + parameters.output_folder = optarg; + break; + case 's': + parameters.scaling_factor = string_to_int(optarg); + break; + case 'h': + print_help(); + break; + default: + abort(); + } + } else { + int i = 0; + parameters.input_files = (char**) malloc ((argc - optind) * sizeof (char*)); + while (optind < argc) { + parameters.input_files[i] = (char*) malloc (strlen(argv[optind]) * sizeof(char)); //TODO Necessary?? + parameters.input_files[i] = argv[optind]; + parameters.input_files_count = i + 1; + optind++; + i++; + } + } + } + + return parameters; +} \ No newline at end of file diff --git a/src/utils.h b/src/utils.h index 83425c6..8887aaf 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,8 +1,11 @@ -#ifndef CCTL_UTILS -#define CCTL_UTILS +#ifndef CCLT_UTILS +#define CCLT_UTILS #include +#define APP_VERSION "1.9.9 BETA" +#define BUILD 20150515 + typedef struct cclt_compress_parameters { int quality; int width; @@ -24,5 +27,8 @@ int string_to_int(char* in_string); void print_help(); void print_progress(int current, int max, char* message); char* get_filename_with_extension(char* full_path); +void jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo); +int mkpath(const char *pathname, mode_t mode); +cclt_compress_parameters parse_arguments(int argc, char* argv[]); #endif