This commit is contained in:
Matteo Paonessa 2017-12-29 23:09:13 +01:00
parent 9fd0fa5f27
commit 45b794d498
15 changed files with 11386 additions and 38 deletions

View File

@ -7,7 +7,7 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# The version number.
set(VERSION_MAJOR 0)
set(VERSION_MINOR 3)
set(VERSION_MINOR 4)
set(VERSION_PATCH 0)
configure_file(

View File

@ -41,13 +41,15 @@ typedef struct cs_jpeg_pars
int quality;
bool exif_copy;
int dct_method;
double scale_factor;
} cs_jpeg_pars;
```
The first 3 parameters matters, in term of compression, while the others will be set by the compressor/decompressor
The first 4 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.
- **dct_method**: one of the turbojpeg DCT flags. Default: TJFLAG_FASTDCT.
- **scale_factor**: the image scaling factor, expressed as double precision number. Default: 1.0.
### PNG
```C
@ -59,15 +61,18 @@ typedef struct cs_png_pars
bool lossy_8;
bool transparent;
int auto_filter_strategy;
double scale_factor;
} cs_png_pars;
```
Those are the zopflipng compression parameters.
Those are the zopflipng compression parameters, except for the last one.
- **iterations**: number of iterations (more means more compression). Default: 10.
- **iteration_large**: number of iterations for large files. Default: 5.
- **block_split_strategy**: filter strategy. Default: 4;
- **lossy_8**: convert 16-bit per channel image to 8-bit per channel. Default: true.
- **transparent**: remove colors behind alpha channel 0. Default: true.
- **auto_filter_strategy**: legacy.
- **scale_factor**: the image scaling factor, expressed as double precision number. Note that PNG cannot be upscaled. Default: 1.0.
## Compilation and Installation
Libcaesium uses cmake to build and install the library. Before compiling, be sure to have all the requisites.
@ -120,4 +125,8 @@ JPEG is a lossy format: that means you will always lose some information after e
100 quality for 10 times will result in a always different image, even though you can't really see the difference.
Libcaesium also supports optimization, by setting the _quality_ to 0. This performs a lossless process, resulting in the same image,
but with a smaller size (10-15% usually).
PNG is lossless, so libcaesium will always perform optimization rather than compression.
PNG is lossless, so libcaesium will always perform optimization rather than compression.
## Resizing
Resizing is partially supported. It is handy but it's almost completely out of the scope of this library.
If you really feel the need to do it within libcaesium you can do so, but I advise you should opt for a different toolset for the best results.

View File

@ -10,8 +10,8 @@ find_library(zopflipng zopflipng /usr/local/lib)
find_library(jpeg jpeg /opt/mozjpeg/lib)
find_library(turbojpeg turbojpeg /opt/mozjpeg/lib)
add_library(caesium SHARED caesium.c error.c utils.c png.c lodepng.c jpeg.c)
add_library(caesium_static STATIC caesium.c error.c utils.c png.c lodepng.c jpeg.c)
add_library(caesium SHARED caesium.c error.c utils.c png.c vendor/lodepng.c jpeg.c)
add_library(caesium_static STATIC caesium.c error.c utils.c png.c vendor/lodepng.c jpeg.c)
set_target_properties(caesium_static PROPERTIES OUTPUT_NAME caesium)

View File

@ -11,6 +11,7 @@ bool cs_compress(const char *input_path, const char *output_path, cs_image_pars
FILE *pInputFile;
image_type type;
bool result = false;
int compression_step_result;
if ((pInputFile = fopen(input_path, "rb")) == NULL) {
display_error(ERROR, 104);
@ -25,10 +26,11 @@ bool cs_compress(const char *input_path, const char *output_path, cs_image_pars
display_error(WARNING, 103);
} else if (type == CS_JPEG) {
if (options->jpeg.quality != 0) {
result = cs_jpeg_compress(output_path, cs_jpeg_decompress(input_path, &options->jpeg), &options->jpeg);
compression_step_result = cs_jpeg_compress(output_path, cs_jpeg_decompress(input_path, &options->jpeg), &options->jpeg);
result = (bool) compression_step_result;
//The output is now the new input for optimization
if (result) {
result = cs_jpeg_optimize(output_path, output_path, &options->jpeg, input_path);
result = cs_jpeg_optimize(compression_step_result == 1 ? output_path : input_path, output_path, &options->jpeg, input_path);
}
} else {
result = cs_jpeg_optimize(input_path, output_path, &options->jpeg, input_path);
@ -45,16 +47,18 @@ void initialize_jpeg_parameters(cs_image_pars *options)
options->jpeg.quality = 0;
options->jpeg.exif_copy = false;
options->jpeg.dct_method = 2048;
options->jpeg.scale_factor = 1.0;
}
void initialize_png_parameters(cs_image_pars *par)
void initialize_png_parameters(cs_image_pars *options)
{
par->png.iterations = 2;
par->png.iterations_large = 1;
par->png.block_split_strategy = 0;
par->png.lossy_8 = true;
par->png.transparent = true;
par->png.auto_filter_strategy = true;
options->png.iterations = 2;
options->png.iterations_large = 1;
options->png.block_split_strategy = 0;
options->png.lossy_8 = true;
options->png.transparent = true;
options->png.auto_filter_strategy = true;
options->png.scale_factor = 1.0;
}
cs_image_pars initialize_parameters()

View File

@ -13,6 +13,7 @@ typedef struct cs_jpeg_pars
int quality;
bool exif_copy;
int dct_method;
double scale_factor;
/*
* Parameters you have no reason to set as they will be
* overwritten during the process
@ -31,6 +32,7 @@ typedef struct cs_png_pars
bool lossy_8;
bool transparent;
int auto_filter_strategy;
double scale_factor;
} cs_png_pars;
typedef struct cs_image_pars

View File

@ -40,7 +40,9 @@ const char *get_error_message(int code)
case 207:
return "Compressor failed";
case 208:
return "Compressor failed";
return "Decompressor failed";
case 209:
return "CMYK images are not fully supported and can only be optimized.";
//PNG related errors
case 300:
@ -49,6 +51,10 @@ const char *get_error_message(int code)
return "Error while optimizing PNG.";
case 303:
return "Error while writing output PNG file.";
case 304:
return "Error while resizing PNG file.";
case 305:
return "PNG scaling factor must be a number greater than 0 and equal or minor to 1.";
default:
return "Unrecognized error.";

View File

@ -3,6 +3,7 @@
#include <string.h>
#include <turbojpeg.h>
#include <limits.h>
#include <math.h>
#include "jpeg.h"
#include "error.h"
@ -127,8 +128,11 @@ bool cs_jpeg_optimize(const char *input_file, const char *output_file, cs_jpeg_p
return true;
}
bool cs_jpeg_compress(const char *output_file, unsigned char *image_buffer, cs_jpeg_pars *options)
int cs_jpeg_compress(const char *output_file, unsigned char *image_buffer, cs_jpeg_pars *options)
{
if (image_buffer == 0) {
return 2;
}
FILE *fp;
tjhandle tjCompressHandle;
unsigned char *output_buffer;
@ -165,7 +169,7 @@ bool cs_jpeg_compress(const char *output_file, unsigned char *image_buffer, cs_j
tjFree(output_buffer);
tjFree(image_buffer);
return true;
return 1;
}
unsigned char *cs_jpeg_decompress(const char *fileName, cs_jpeg_pars *options)
@ -195,13 +199,18 @@ unsigned char *cs_jpeg_decompress(const char *fileName, cs_jpeg_pars *options)
tjDecompressHeader3(tjDecompressHandle, sourceJpegBuffer, (unsigned long) sourceJpegBufferSize, &fileWidth, &fileHeight,
&jpegSubsamp, &colorSpace);
options->width = fileWidth;
options->height = fileHeight;
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);
options->subsample = (enum TJSAMP) jpegSubsamp;
options->color_space = colorSpace;
unsigned char *temp = tjAlloc(options->width * options->height * tjPixelSize[options->color_space]);
unsigned char *temp = tjAlloc(options->width * options->height * tjPixelSize[colorSpace]);
result = tjDecompress2(tjDecompressHandle,
sourceJpegBuffer,
@ -210,7 +219,7 @@ unsigned char *cs_jpeg_decompress(const char *fileName, cs_jpeg_pars *options)
options->width,
0,
options->height,
options->color_space,
colorSpace,
options->dct_method);
if (result == -1) {
display_error(ERROR, 208);

View File

@ -9,7 +9,7 @@ bool cs_jpeg_optimize(const char *input_file, const char *output_file, cs_jpeg_p
struct jpeg_decompress_struct cs_get_markers(const char *input);
bool cs_jpeg_compress(const char *output_file, unsigned char *image_buffer, cs_jpeg_pars *options);
int 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);

View File

@ -1,12 +1,17 @@
//
// Created by Matteo Paonessa on 08/11/16.
//
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STBIW_ASSERT(x)
#include <stdlib.h>
#include <zopflipng/zopflipng_lib.h>
#include "vendor/stb_image.h"
#include "vendor/stb_image_write.h"
#include "vendor/stb_image_resize.h"
#include "png.h"
#include "lodepng.h"
#include "vendor/lodepng.h"
#include "error.h"
bool cs_png_optimize(const char *input, const char *output, cs_png_pars *options)
@ -15,6 +20,17 @@ bool cs_png_optimize(const char *input, const char *output, cs_png_pars *options
CZopfliPNGOptions png_options;
int error_code = 0;
if (options->scale_factor > 0.0 && options->scale_factor < 1.0) {
result = cs_png_resize(input, output, options->scale_factor);
if (!result) {
display_error(ERROR, 304);
return result;
}
} else if (options->scale_factor != 1.0) {
display_error(ERROR, 305);
return false;
}
CZopfliPNGSetDefaults(&png_options);
unsigned char *orig_buffer;
@ -32,17 +48,19 @@ 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) {
if (lodepng_load_file(&orig_buffer, &orig_buffer_size, options->scale_factor == 1 ? input : output) != 0) {
error_code = 300;
goto cleanup;
}
if (CZopfliPNGOptimize(orig_buffer,
orig_buffer_size,
&png_options,
0,
&resultpng,
&resultpng_size) != 0) {
int code = CZopfliPNGOptimize(orig_buffer,
orig_buffer_size,
&png_options,
0,
&resultpng,
&resultpng_size);
if (code != 0) {
error_code = 301;
goto cleanup;
}
@ -62,3 +80,33 @@ bool cs_png_optimize(const char *input, const char *output, cs_png_pars *options
}
return result;
}
bool cs_png_resize(const char *input, const char *output, double factor)
{
unsigned char *input_pixels;
unsigned char *output_pixels;
int w, h;
int n;
int out_w, out_h;
int result;
input_pixels = stbi_load(input, &w, &h, &n, 0);
if (input_pixels == 0) {
return false;
}
out_w = (int) round(w * factor);
out_h = (int) round(h * factor);
output_pixels = (unsigned char *) malloc(out_w * out_h * n);
result = stbir_resize_uint8(input_pixels, w, h, 0, output_pixels, out_w, out_h, 0, n);
if (!result) {
free(output_pixels);
return false;
}
result = stbi_write_png(output, out_w, out_h, n, output_pixels, 0);
if (!result) {
return false;
}
return true;
}

View File

@ -1,7 +1,3 @@
//
// Created by Matteo Paonessa on 08/11/16.
//
#ifndef LIBCAESIUM_PNG_H
#define LIBCAESIUM_PNG_H
@ -9,4 +5,6 @@
bool cs_png_optimize(const char *input, const char *output, cs_png_pars *options);
bool cs_png_resize(const char *input, const char *output, double factor);
#endif //LIBCAESIUM_PNG_H

7187
caesium/vendor/stb_image.h vendored Executable file

File diff suppressed because it is too large Load Diff

2627
caesium/vendor/stb_image_resize.h vendored Executable file

File diff suppressed because it is too large Load Diff

1458
caesium/vendor/stb_image_write.h vendored Executable file

File diff suppressed because it is too large Load Diff