diff --git a/src/Makefile.am b/src/Makefile.am index a2200eb..75a6705 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ bin_PROGRAMS = caesiumclt caesiumclt_SOURCES = main.c jpeg.c compresshelper.c utils.c png.c lodepng.c caesiumclt_CFLAGS = -Wall -D_FILE_OFFSET_BITS=64 -std=c99 -caesiumclt_LDADD = -lm \ No newline at end of file +caesiumclt_LDADD = -lm diff --git a/src/compresshelper.c b/src/compresshelper.c index e76f612..c0666f9 100644 --- a/src/compresshelper.c +++ b/src/compresshelper.c @@ -12,20 +12,33 @@ #include "jpeg.h" #include "png.h" -//TODO CRITICAL It does not recognize single files as input. Why. +void initialize_jpeg_parameters(cclt_parameters* par) { + par->jpeg.quality = 0; + par->jpeg.width = 0; + par->jpeg.height = 0; + par->jpeg.color_space = TJCS_RGB; + par->jpeg.dct_method = TJFLAG_FASTDCT; + par->jpeg.exif_copy = 0; + par->jpeg.lossless = 0; +} -cclt_compress_parameters initialize_compression_parameters() { - cclt_compress_parameters par; +void initialize_png_parameters(cclt_parameters* par) { + par->png.iterations = 15; + par->png.iterations_large = 5; + par->png.block_split_strategy = 4; + par->png.lossy_8 = 1; + par->png.transparent = 1; + par->png.auto_filter_strategy = 1; +} + +cclt_parameters initialize_compression_parameters() { + cclt_parameters par; + + initialize_jpeg_parameters(&par); + initialize_png_parameters(&par); - par.quality = 0; - par.width = 0; - par.height = 0; - par.color_space = TJCS_RGB; - par.dct_method = TJFLAG_FASTDCT; par.output_folder = NULL; - par.exif_copy = 0; - par.lossless = 0; par.input_files_count = 0; par.recursive = 0; par.input_files = NULL; @@ -34,21 +47,20 @@ cclt_compress_parameters initialize_compression_parameters() { return par; } -cclt_compress_parameters parse_arguments(int argc, char* argv[]) { +cclt_parameters parse_arguments(int argc, char* argv[]) { //Initialize default params - cclt_compress_parameters parameters = initialize_compression_parameters(); + cclt_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); + printf("CaesiumCLT - Caesium Command Line Tools - Version %s (Build: %d)\n", APP_VERSION, BUILD); exit(0); break; case '?': - //TODO if -o not specified or empty, use current. Useful? if (optopt == 'q' || optopt == 'o' || optopt == 's') { fprintf (stderr, "Option -%c requires an argument.\n", optopt); //Arguments without values @@ -65,13 +77,13 @@ cclt_compress_parameters parse_arguments(int argc, char* argv[]) { fprintf(stderr, "Parameter expected.\n"); break; case 'q': - parameters.quality = string_to_int(optarg); + parameters.jpeg.quality = string_to_int(optarg); break; case 'e': - parameters.exif_copy = 1; + parameters.jpeg.exif_copy = 1; break; case 'l': - parameters.lossless = 1; + parameters.jpeg.lossless = 1; break; case 'o': parameters.output_folder = optarg; @@ -116,19 +128,19 @@ cclt_compress_parameters parse_arguments(int argc, char* argv[]) { return parameters; } -int cclt_compress_routine(char* input, char* output, cclt_compress_parameters* pars) { +int cclt_compress_routine(char* input, char* output, cclt_parameters* pars) { enum image_type type = detect_image_type(input); - if (type == JPEG && pars->lossless == 0) { - cclt_jpeg_compress(output, cclt_jpeg_decompress(input, pars), pars); - cclt_jpeg_optimize(output, output, pars->exif_copy, input); - } else if (type == JPEG && pars->lossless != 0) { - cclt_jpeg_optimize(input, output, pars->exif_copy, input); + if (type == JPEG && pars->jpeg.lossless == 0) { + cclt_jpeg_compress(output, cclt_jpeg_decompress(input, &pars->jpeg), &pars->jpeg); + cclt_jpeg_optimize(output, output, pars->jpeg.exif_copy, input); + } else if (type == JPEG && pars->jpeg.lossless != 0) { + cclt_jpeg_optimize(input, output, pars->jpeg.exif_copy, input); } else if (type == PNG) { //Give a message to the user if he set a quality for PNGs - if (pars->quality != 0) { + if (pars->jpeg.quality != 0) { printf("PNG file, ignoring quality parameter.\n"); } - cclt_png_optimize(input, output); + cclt_png_optimize(input, output, &pars->png); } else { printf("Unknown file type.\n"); return -1; diff --git a/src/compresshelper.h b/src/compresshelper.h index 1966b10..13fda9c 100644 --- a/src/compresshelper.h +++ b/src/compresshelper.h @@ -3,8 +3,8 @@ #include "utils.h" -cclt_compress_parameters initialize_compression_parameters(); -cclt_compress_parameters parse_arguments(int argc, char* argv[]); -int cclt_compress_routine(char* input, char* output, cclt_compress_parameters* pars); //Returns -1 if the file type is unknown +cclt_parameters initialize_compression_parameters(); +cclt_parameters parse_arguments(int argc, char* argv[]); +int cclt_compress_routine(char* input, char* output, cclt_parameters* pars); //Returns -1 if the file type is unknown -#endif \ No newline at end of file +#endif diff --git a/src/jpeg.c b/src/jpeg.c index 4aed462..ea57de7 100644 --- a/src/jpeg.c +++ b/src/jpeg.c @@ -30,10 +30,10 @@ struct jpeg_decompress_struct cclt_get_markers(char* input) { exit(-13); } - //Create the IO instance for the input file + //Create the IO instance for the input file jpeg_stdio_src(&einfo, fp); - //Save EXIF info + //Save EXIF info for (int m = 0; m < 16; m++) { jpeg_save_markers(&einfo, JPEG_APP0 + m, 0xFFFF); } @@ -55,7 +55,7 @@ int cclt_jpeg_optimize(char* input_file, char* output_file, int exif_flag, char* struct jpeg_decompress_struct srcinfo; struct jpeg_compress_struct dstinfo; - //Error handling + //Error handling struct jpeg_error_mgr jsrcerr, jdsterr; //Input/Output array coefficents @@ -69,7 +69,7 @@ int cclt_jpeg_optimize(char* input_file, char* output_file, int exif_flag, char* jpeg_create_compress(&dstinfo); - //Open the input file + //Open the input file fp = fopen(input_file, "r"); //Check for errors @@ -82,19 +82,20 @@ int cclt_jpeg_optimize(char* input_file, char* output_file, int exif_flag, char* //Create the IO istance for the input file jpeg_stdio_src(&srcinfo, fp); - //Save EXIF info + //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 + //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); + //jcopy_markers_setup(&srcinfo, copyoption); //Copy parameters jpeg_copy_critical_parameters(&srcinfo, &dstinfo); @@ -116,7 +117,7 @@ int cclt_jpeg_optimize(char* input_file, char* output_file, int exif_flag, char* //CRITICAL - This is the optimization step dstinfo.optimize_coding = TRUE; - //Progressive + //Progressive jpeg_simple_progression(&dstinfo); //Set the output file parameters @@ -125,19 +126,19 @@ int cclt_jpeg_optimize(char* input_file, char* output_file, int exif_flag, char* //Actually write the coefficents jpeg_write_coefficients(&dstinfo, dst_coef_arrays); - //Write EXIF + //Write EXIF if (exif_flag == 1) { if (strcmp(input_file, exif_src) == 0) { jcopy_markers_execute(&srcinfo, &dstinfo); } else { - //For standard compression EXIF data + //For standard compression EXIF data struct jpeg_decompress_struct einfo = cclt_get_markers(exif_src); jcopy_markers_execute(&einfo, &dstinfo); jpeg_destroy_decompress(&einfo); } } - //Finish and free + //Finish and free jpeg_finish_compress(&dstinfo); jpeg_destroy_compress(&dstinfo); (void) jpeg_finish_decompress(&srcinfo); @@ -149,7 +150,7 @@ int cclt_jpeg_optimize(char* input_file, char* output_file, int exif_flag, char* return 0; } -void cclt_jpeg_compress(char* output_file, unsigned char* image_buffer, cclt_compress_parameters* pars) { +void cclt_jpeg_compress(char* output_file, unsigned char* image_buffer, cclt_jpeg_parameters* pars) { FILE* fp; tjhandle tjCompressHandle; unsigned char* output_buffer; @@ -160,8 +161,8 @@ void cclt_jpeg_compress(char* output_file, unsigned char* image_buffer, cclt_com //Check for errors //TODO Use UNIX error messages if (fp == NULL) { - printf("OUTPUT: Failed to open output \"%s\"\n", output_file); - return; + printf("OUTPUT: Failed to open output \"%s\"\n", output_file); + return; } output_buffer = NULL; @@ -169,16 +170,16 @@ void cclt_jpeg_compress(char* output_file, unsigned char* image_buffer, cclt_com //TODO Error checks tjCompress2(tjCompressHandle, - image_buffer, - pars->width, - 0, - pars->height, - pars->color_space, - &output_buffer, - &output_size, - pars->subsample, - pars->quality, - pars->dct_method); + image_buffer, + pars->width, + 0, + pars->height, + pars->color_space, + &output_buffer, + &output_size, + pars->subsample, + pars->quality, + pars->dct_method); fwrite(output_buffer, output_size, 1, fp); @@ -188,49 +189,49 @@ void cclt_jpeg_compress(char* output_file, unsigned char* image_buffer, cclt_com } -unsigned char* cclt_jpeg_decompress(char* fileName, cclt_compress_parameters* pars) { +unsigned char* cclt_jpeg_decompress(char* fileName, cclt_jpeg_parameters* pars) { //TODO I/O Error handling - FILE *file = NULL; - int res = 0; - long int sourceJpegBufferSize = 0; - unsigned char* sourceJpegBuffer = NULL; - tjhandle tjDecompressHandle; - int fileWidth = 0, fileHeight = 0, jpegSubsamp = 0, colorSpace = 0; + FILE *file = NULL; + int res = 0; + long int sourceJpegBufferSize = 0; + unsigned char* sourceJpegBuffer = NULL; + tjhandle tjDecompressHandle; + int fileWidth = 0, fileHeight = 0, jpegSubsamp = 0, colorSpace = 0; - //TODO No error checks here - file = fopen(fileName, "rb"); - res = fseek(file, 0, SEEK_END); - sourceJpegBufferSize = ftell(file); - sourceJpegBuffer = tjAlloc(sourceJpegBufferSize); + //TODO No error checks here + 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 = tjDecompressHeader3(tjDecompressHandle, sourceJpegBuffer, sourceJpegBufferSize, &fileWidth, &fileHeight, &jpegSubsamp, &colorSpace); + res = fseek(file, 0, SEEK_SET); + res = fread(sourceJpegBuffer, (long)sourceJpegBufferSize, 1, file); + tjDecompressHandle = tjInitDecompress(); + res = tjDecompressHeader3(tjDecompressHandle, sourceJpegBuffer, sourceJpegBufferSize, &fileWidth, &fileHeight, &jpegSubsamp, &colorSpace); - pars->width = fileWidth; - pars->height = fileHeight; + pars->width = fileWidth; + pars->height = fileHeight; - pars->subsample = jpegSubsamp; - pars->color_space = colorSpace; + pars->subsample = jpegSubsamp; + pars->color_space = colorSpace; - unsigned char* temp = tjAlloc(pars->width * pars->height * tjPixelSize[pars->color_space]); + unsigned char* temp = tjAlloc(pars->width * pars->height * tjPixelSize[pars->color_space]); - res = tjDecompress2(tjDecompressHandle, - sourceJpegBuffer, - sourceJpegBufferSize, - temp, - pars->width, - 0, - pars->height, - pars->color_space, - pars->dct_method); + res = tjDecompress2(tjDecompressHandle, + sourceJpegBuffer, + sourceJpegBufferSize, + temp, + pars->width, + 0, + pars->height, + pars->color_space, + pars->dct_method); - //fwrite(temp, pars->width * pars->height * tjPixelSize[pars->color_space], 1, fopen("/Users/lymphatus/Desktop/tmp/compresse/ccc", "w")); + //fwrite(temp, pars->width * pars->height * tjPixelSize[pars->color_space], 1, fopen("/Users/lymphatus/Desktop/tmp/compresse/ccc", "w")); - tjDestroy(tjDecompressHandle); + tjDestroy(tjDecompressHandle); - return temp; + return temp; } diff --git a/src/jpeg.h b/src/jpeg.h index 770c94f..c5677b8 100644 --- a/src/jpeg.h +++ b/src/jpeg.h @@ -7,9 +7,9 @@ int cclt_jpeg_optimize(char* input_file, char* output_file, int exif_flag, char* exif_src); struct jpeg_decompress_struct cclt_get_markers(char* input); -void cclt_jpeg_compress(char* output_file, unsigned char* image_buffer, cclt_compress_parameters* pars); -unsigned char* cclt_jpeg_decompress(char* fileName, cclt_compress_parameters* pars); +void cclt_jpeg_compress(char* output_file, unsigned char* image_buffer, cclt_jpeg_parameters* pars); +unsigned char* cclt_jpeg_decompress(char* fileName, cclt_jpeg_parameters* pars); void jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo); -#endif \ No newline at end of file +#endif diff --git a/src/main.c b/src/main.c index 314f63a..81195df 100755 --- a/src/main.c +++ b/src/main.c @@ -28,7 +28,7 @@ //TODO If the output is INSIDE the folder we are passing as input, ignore it or we're gonna go in a infinite loop -void cclt_start(cclt_compress_parameters* pars, off_t* i_t_size, off_t* o_t_size) { +void cclt_start(cclt_parameters* pars, off_t* i_t_size, off_t* o_t_size) { struct stat st_buf; int i = 0; @@ -111,15 +111,15 @@ int main (int argc, char *argv[]) { off_t i_t_size = 0, o_t_size = 0; //Parse arguments - cclt_compress_parameters pars = parse_arguments(argc, argv); + cclt_parameters pars = parse_arguments(argc, argv); //Either -l or -q must be set but not together - if (!((pars.lossless == 1) ^ (pars.quality > 0))) { + if (!((pars.jpeg.lossless == 1) ^ (pars.jpeg.quality > 0))) { //Both or none are set - if (pars.lossless == 1 && pars.quality > 0) { + if (pars.jpeg.lossless == 1 && pars.jpeg.quality > 0) { fprintf(stderr, "-l option can't be used with -q. Either use one or the other. Aborting.\n"); exit(-1); - } else if (pars.lossless == 0 && pars.quality <= 0) { + } else if (pars.jpeg.lossless == 0 && pars.jpeg.quality <= 0) { fprintf(stderr, "Either -l or -q must be set. Aborting.\n"); print_help(); exit(-2); @@ -127,7 +127,7 @@ int main (int argc, char *argv[]) { } else { //One of them is set //If -q is set check it is within the 1-100 range - if (!(pars.quality >= 1 && pars.quality <= 100) && pars.lossless == 0) { + if (!(pars.jpeg.quality >= 1 && pars.jpeg.quality <= 100) && pars.jpeg.lossless == 0) { fprintf(stderr, "Quality must be within a [1-100] range. Aborting.\n"); exit(-3); } @@ -155,18 +155,15 @@ int main (int argc, char *argv[]) { clock_t start = clock(), diff; //We need the file list right here cclt_start(&pars, &i_t_size, &o_t_size); - /*for (int i = 0; i < pars.input_files_count; i++) { - printf("FILE %d: %s\n", i, pars.input_files[i]); - }*/ diff = clock() - start; fprintf(stdout, "-------------------------------\nCompression completed in %lum%lus\n%s -> %s [%.2f%% | %s]\n", - diff / CLOCKS_PER_SEC / 60, - diff / CLOCKS_PER_SEC % 60, - get_human_size((long) i_t_size), - get_human_size((long) o_t_size), - ((float) o_t_size - i_t_size) * 100 / i_t_size, - get_human_size(((long) o_t_size - i_t_size))); + diff / CLOCKS_PER_SEC / 60, + diff / CLOCKS_PER_SEC % 60, + get_human_size((long) i_t_size), + get_human_size((long) o_t_size), + ((float) o_t_size - i_t_size) * 100 / i_t_size, + get_human_size(((long) o_t_size - i_t_size))); return 0; } diff --git a/src/png.c b/src/png.c index 355e965..32d4e39 100644 --- a/src/png.c +++ b/src/png.c @@ -6,7 +6,7 @@ #include #include "png.h" -void cclt_png_optimize(char* input, char* output) { +void cclt_png_optimize(char* input, char* output, cclt_png_parameters* pars) { //TODO Error handling CZopfliPNGOptions png_options; @@ -18,35 +18,35 @@ void cclt_png_optimize(char* input, char* output) { unsigned char* resultpng; size_t resultpng_size; - png_options.num_iterations = 15; - png_options.num_iterations_large = 5; - png_options.block_split_strategy = 4; + png_options.num_iterations = pars->iterations; + png_options.num_iterations_large = pars->iterations_large; + png_options.block_split_strategy = pars->block_split_strategy; - png_options.lossy_8bit = 1; - png_options.lossy_transparent = 1; + png_options.lossy_8bit = pars->lossy_8; + png_options.lossy_transparent = pars->transparent; - png_options.auto_filter_strategy = 1; + png_options.auto_filter_strategy = pars->auto_filter_strategy; - if (lodepng_load_file(&orig_buffer, &orig_buffer_size, input) != 0) { - fprintf(stderr, "Error while loading PNG. Aborting.\n"); - exit(-16); - } + if (lodepng_load_file(&orig_buffer, &orig_buffer_size, input) != 0) { + fprintf(stderr, "Error while loading PNG. Aborting.\n"); + exit(-16); + } - if (CZopfliPNGOptimize(orig_buffer, - orig_buffer_size, - &png_options, - 0, - &resultpng, - &resultpng_size) != 0) { - fprintf(stderr, "Error while optimizing PNG. Aborting.\n"); - exit(-17); - } + if (CZopfliPNGOptimize(orig_buffer, + orig_buffer_size, + &png_options, + 0, + &resultpng, + &resultpng_size) != 0) { + fprintf(stderr, "Error while optimizing PNG. Aborting.\n"); + exit(-17); + } - if (lodepng_save_file(resultpng, resultpng_size, output) != 0) { - fprintf(stderr, "Error while writing PNG. Aborting.\n"); - exit(-18); - } + if (lodepng_save_file(resultpng, resultpng_size, output) != 0) { + fprintf(stderr, "Error while writing PNG. Aborting.\n"); + exit(-18); + } - free(orig_buffer); - free(resultpng); + free(orig_buffer); + free(resultpng); } diff --git a/src/png.h b/src/png.h index 81e5fb4..e721a33 100644 --- a/src/png.h +++ b/src/png.h @@ -1,6 +1,8 @@ #ifndef CCLT_PNG #define CCLT_PNG -void cclt_png_optimize(char* input, char* output); +#include "utils.h" -#endif \ No newline at end of file +void cclt_png_optimize(char* input, char* output, cclt_png_parameters* pars); + +#endif diff --git a/src/utils.c b/src/utils.c index 9761651..e2edcbf 100644 --- a/src/utils.c +++ b/src/utils.c @@ -30,14 +30,14 @@ int string_to_int(char* in_string) { //Check errors if ((errno == ERANGE) || (errno != 0 && value == 0)) { - perror("strtol"); - exit(-8); - } + perror("strtol"); + exit(-8); + } if (endptr == in_string) { - fprintf(stderr, "Parse error: No digits were found for -q option. Aborting.\n"); - exit(-7); - } + fprintf(stderr, "Parse error: No digits were found for -q option. Aborting.\n"); + exit(-7); + } return value; } @@ -86,7 +86,7 @@ int mkpath(const char *pathname, mode_t mode) { return -1; } -void scan_folder(cclt_compress_parameters* parameters, char* basedir, int recur) { +void scan_folder(cclt_parameters* parameters, char* basedir, int recur) { //TODO CRITIAL Pass list as 1st parameter DIR *dir; struct dirent *ent; diff --git a/src/utils.h b/src/utils.h index b2f3fb7..f4e910e 100644 --- a/src/utils.h +++ b/src/utils.h @@ -6,23 +6,38 @@ #include #define APP_VERSION "0.9.1-beta" -#define BUILD 20160116 +#define BUILD 20160421 -typedef struct cclt_compress_parameters { +typedef struct cclt_jpeg_parameters { int quality; int width; int height; - char* output_folder; int color_space; int dct_method; int exif_copy; int lossless; + enum TJSAMP subsample; +} cclt_jpeg_parameters; + +typedef struct cclt_png_parameters { + int iterations; + int iterations_large; + int block_split_strategy; + int lossy_8; + int transparent; + int auto_filter_strategy; +} cclt_png_parameters; + +typedef struct cclt_parameters { + cclt_jpeg_parameters jpeg; + cclt_png_parameters png; + + char* output_folder; char** input_files; int input_files_count; - enum TJSAMP subsample; int recursive; int structure; -} cclt_compress_parameters; +} cclt_parameters; enum image_type { JPEG, @@ -35,7 +50,7 @@ void print_help(); int mkpath(const char *pathname, mode_t mode); enum image_type detect_image_type(char* path); int is_directory(const char *file_path); -void scan_folder(cclt_compress_parameters* parameters, char* basedir, int recur); +void scan_folder(cclt_parameters* parameters, char* basedir, int recur); char* get_human_size(long size); #endif