Rust switch

This commit is contained in:
Matteo Paonessa 2021-10-23 17:18:52 +02:00
parent 2c1256ebbf
commit 0277984dfa
45 changed files with 999 additions and 19196 deletions

24
.github/workflows/rust.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Rust
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose --release
- name: Run tests
run: cargo test --verbose --release

123
.gitignore vendored
View File

@ -1,115 +1,14 @@
# Object files
*.o
*.ko
*.obj
*.elf
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Precompiled Headers
*.gch
*.pch
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# Libraries
*.lib
*.a
*.la
*.lo
# These are backup files generated by rustfmt
**/*.rs.bk
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
# CLion
.idea/*
# Build
cmake-build-*
build
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/workspace.xml
.idea/tasks.xml
# Sensitive or high-churn files:
.idea/dataSources/
.idea/dataSources.ids
.idea/dataSources.xml
.idea/dataSources.local.xml
.idea/sqlDataSources.xml
.idea/dynamic.xml
.idea/uiDesigner.xml
# Gradle:
.idea/gradle.xml
.idea/libraries
# Mongo Explorer plugin:
.idea/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
mozjpeg
zopfli
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

6
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
# Default ignored files
/shelf/
/workspace.xml
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

13
.idea/caesium.iml Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
<excludeFolder url="file://$MODULE_DIR$/dssim/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/caesium.iml" filepath="$PROJECT_DIR$/.idea/caesium.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@ -1,30 +0,0 @@
language: c
os:
- linux
- osx
dist: bionic
osx_image: xcode11.2
compiler:
- gcc
- clang
before_install:
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install nasm; fi
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then alias nasm=/usr/local/bin/nasm; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install libtool autoconf git nasm pkg-config cmake; fi
- chmod +x install.sh
- ./install.sh
before_script:
- mkdir build
- cd build
- cmake ..
script: make
after_success:
- demo/caesiumd -v

View File

@ -1,59 +0,0 @@
cmake_minimum_required(VERSION 2.8.12)
project(libcaesium)
set(CMAKE_MACOSX_RPATH 1)
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
# The version number.
set(VERSION_MAJOR 0)
set(VERSION_MINOR 5)
set(VERSION_PATCH 2)
if (DEFINED VERBOSE)
set(VERBOSE ${VERBOSE})
else ()
set(VERBOSE 0)
endif ()
configure_file(
"caesium/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
include_directories("${PROJECT_BINARY_DIR}")
if (APPLE)
set(MOZJPEG_DEFAULT_PATH /opt/mozjpeg)
set(ZOPFLI_DEFAULT_PATH /usr/local)
elseif (WIN32)
set(MOZJPEG_DEFAULT_PATH C:\\mozjpeg)
set(ZOPFLI_DEFAULT_PATH C:\\zopfli)
else ()
set(MOZJPEG_DEFAULT_PATH /opt/mozjpeg)
set(ZOPFLI_DEFAULT_PATH /usr)
endif ()
if (NOT DEFINED MOZJPEG_PATH)
set(MOZJPEG_PATH ${MOZJPEG_DEFAULT_PATH})
endif()
if (NOT DEFINED ZOPFLI_PATH)
set(ZOPFLI_PATH ${ZOPFLI_DEFAULT_PATH})
endif()
include_directories(${MOZJPEG_PATH}/include)
include_directories(${ZOPFLI_PATH}/include)
if (EXISTS ${MOZJPEG_PATH}/lib64)
link_directories(${MOZJPEG_PATH}/lib64)
else ()
link_directories(${MOZJPEG_PATH}/lib)
endif ()
link_directories(${ZOPFLI_PATH}/lib)
add_subdirectory(caesium)
add_subdirectory(demo)

48
Cargo.toml Normal file
View File

@ -0,0 +1,48 @@
[package]
name = "libcaesium"
version = "0.6.0"
authors = ["Matteo Paonessa <matteo.paonessa@gmail.com>"]
edition = "2018"
categories = ["multimedia::images"]
keywords = [
"compression",
"library",
"jpeg",
"png",
"gif",
"webp"
]
description = "A lossy/lossless image compression library."
readme = "README.md"
exclude = [
"tests/*",
]
homepage = "https://github.com/Lymphatus/libcaesium"
repository = "https://github.com/Lymphatus/libcaesium"
license = "Apache-2.0"
[dependencies]
mozjpeg = "0.9.1"
mozjpeg-sys = "1.0.0"
oxipng = "5.0"
libc = "0.2.76"
wasm-bindgen = "0.2"
gifsicle = "1.92.5"
webp = "0.2.0"
infer = "0.5.0"
[dev-dependencies]
dssim = "2.11.2"
load_image = { version = "2.12.1", features = ["static"] }
imgref = "1.7.0"
lodepng = "3.0.0"
kamadak-exif = "0.5.0"
[lib]
name = "caesium"
path = "src/lib.rs"
crate-type = ["lib", "dylib"]
test = true
doctest = false
bench = false
doc = false

211
README.md
View File

@ -1,128 +1,131 @@
# libcaesium
[![Build Status](https://travis-ci.org/Lymphatus/libcaesium.svg?branch=master)](https://travis-ci.org/Lymphatus/libcaesium)
Libcaesium is a simple library performing JPEG and PNG compression/optimization using [mozjpeg](https://github.com/mozilla/mozjpeg) and [zopfli](https://github.com/google/zopfli).
Libcaesium is a simple library performing JPEG, PNG, WebP and GIF (experimental) compression/optimization written in Rust, with a C interface.\
**IMPORTANT**: starting from v0.6.0 the library is written in Rust and no longer in C. There's a C interface, but it's not backward compatible with the <0.6.0.
## Download
Binaries not available yet. Please refer to the compilation section below.
## Basic usage
Libcaesium exposes one single function to compress, auto-detecting the input file type:
```C
bool cs_compress(const char *input,
const char *output,
cs_image_pars *options,
int* err_n);
## Usage in Rust
Libcaesium exposes one single function, auto-detecting the input file type:
```Rust
pub fn compress(
input_path: String,
output_path: String,
parameters: CSParameters
) -> Result<(), Box<dyn Error>>
```
#### Parameters
**input** - input file path
**output** - output file path
**options** - pointer to the options struct, containing compression parameters (see below)
**err_n** - pointer to an integer that will contain the error code if something went wrong during compression
#### Return value
**true** if the compression has successfully ended, or **false** if any error occurs. If any error occurred, the **err_n**
variable will contain the error code. See `error.h` for further info.
## Compression options
Libcaesium supports a few compression parameters for each JPEG and PNG.
You need to initialize the default values before compressing by calling `initialize_parameters()`.
- `input_path` - input file path (full filename)
- `output_path` - output file path (full filename)
- `parameters` - options struct, containing compression parameters (see below)
### Compression options
Libcaesium supports a few compression parameters for each file it supports.
They are defined into a top level struct containing each supported file parameters, as follows:
```C
typedef struct cs_image_pars
{
cs_jpeg_pars jpeg;
cs_png_pars png;
} cs_image_pars;
```Rust
pub struct CSParameters {
pub jpeg: jpeg::Parameters,
pub png: png::Parameters,
pub gif: gif::Parameters,
pub webp: webp::Parameters,
pub keep_metadata: bool,
pub optimize: bool,
}
```
### JPEG
```C
typedef struct cs_jpeg_pars
{
int quality;
bool exif_copy;
int dct_method;
double scale_factor;
} cs_jpeg_pars;
```
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.
Each file type has its own options, but the last two are generic:
- `keep_metadata`: will keep metadata information for any supported type. JPEG and PNG supported. Default `false`.
- `optimize`: forces optimization, when available. With this option enabled the compression will be lossless. JPEG, PNG and WebP supported. Default `false`.
### PNG
```C
typedef struct cs_png_pars
{
int iterations;
int iterations_large;
int block_split_strategy;
bool lossy_8;
bool transparent;
int auto_filter_strategy;
double scale_factor;
} cs_png_pars;
#### jpeg
```Rust
pub struct Parameters {
pub quality: u32,
}
```
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.
- `quality`: in a range from 1 to 100, the quality of the resulting image. Default `80`.
#### png
```Rust
pub struct Parameters {
pub oxipng: oxipng::Options,
pub level: u32,
pub force_zopfli: bool
}
```
- `oxipng`: oxipng options. Should be left as default unless you want to do something advanced. Refer to [oxipng](https://github.com/shssoichiro/oxipng) for documentation.
- `level`: level of optimization, from 0 to 6. Increasing the level will result in a smaller file, at the cost of computation time. If the optimization flag is `true`, the level is set to `6`. Default: `3`.
- `force_zopfli`: if `optimization` is `true` and this option is also `true`, will use zopfli algorithm for compression, resulting in a smaller image but it may take minutes to finish the process. Default `false`.
#### gif
GIF support is experimental, has many know issues and does not support optimization. Expect bugs (especially on Windows).
```Rust
pub struct Parameters {
pub quality: u32,
}
```
- `quality`: in a range from 0 to 100, the quality of the resulting image. If the optimization flag is `true`, the level is set to `100`. Default: `80`.
#### webp
WebP compression is tricky. The format is already well optimized and using the `optimize` flag will probably result in a bigger image.
```Rust
pub struct Parameters {
pub quality: u32,
}
```
- `quality`: in a range from 0 to 100, the quality of the resulting image. If the optimization flag is `true`, this option will be ignored. Default: `60`.
## Usage in C
Libcaesium exposes one single C function, auto-detecting the input file type:
```Rust
pub extern fn c_compress(
input_path: *const c_char,
output_path: *const c_char,
params: C_CSParameters
) -> bool
```
#### Parameters
- `input_path` - input file path (full filename)
- `output_path` - output file path (full filename)
- `parameters` - options struct, containing compression parameters (see below)
-
#### Return
`true` if all goes well, `false` otherwise.
### Compression options
The C options struct is slightly different from the Rust one:
```Rust
#[repr(C)]
pub struct C_CSParameters {
pub keep_metadata: bool,
pub jpeg_quality: u32,
pub png_level: u32,
pub png_force_zopfli: bool,
pub gif_quality: u32,
pub webp_quality: u32,
pub optimize: bool,
}
```
The option description is the same as the Rust counterpart.
## Download
Binaries not available. Please refer to the compilation section below.
## Compilation and Installation
Libcaesium uses cmake to build and install the library. Before compiling, be sure to have all the requisites.
Libcaesium requires [mozjpeg](https://github.com/mozilla/mozjpeg) and [zopfli](https://github.com/google/zopfli) installed as shared/static libraries.
Please refer to their own documentation for detailed instructions.
You can also enable the verbose output, which will print on stderr if anything goes wrong, by using the `-DVERBOSE=1` flag during compilation.
Compilation is available for all supported platforms: Windows, MacOS and Linux.
### OS X/Linux
##### Requirements
Be sure you have the build tools
###### Linux
`$ sudo apt-get install libtool autoconf git nasm pkg-config cmake libpng-dev`
###### OSX
`$ brew install nasm cmake`
Get the code with
`$ git clone https://github.com/Lymphatus/libcaesium.git`
If you don't have `mozjpeg` and `zopfli` you should run
```bash
$ cd libcaesium
$ ./install.sh
```
which will install the requirements.
##### Compile
Provided you have all the requirements, building and installing from git is as simple as typing
```bash
$ mkdir build
$ cd build
$ cmake ..
$ make
$ sudo make install
cargo build --release
```
This will compile the Caesium library, the required header and a small demo application named _caesiumd_.
Note: if you don't use the `--release` flag, the PNG optimizations can take a very long time to complete, especially using the zopfli algorithm.
### Windows
Compiling on Windows is somehow tricky. You can achieve it with MinGW (tested) or Cygwin (not tested), but it's better to stick with the binaries provided.
The result will be a dynamic library usable by external applications through its C interface.
## Compression vs Optimization
JPEG is a lossy format: that means you will always lose some information after each compression. So, compressing a file with
100 quality for 10 times will result in a always different image, even though you can't really see the difference.
100 quality for 10 times will result in an 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).
but with a smaller size (10-12% usually).
PNG is lossless, so libcaesium will always perform optimization rather than compression.
GIF optimization is possible, but currently not supported.
WebP optimization is also possible, but it will probably result in a bigger output file as it's well suited to losslessly convert from PNG or JPEG.
## 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.
Resizing is no longer supported since 0.6.0.

View File

@ -1,21 +0,0 @@
if (NOT WIN32)
set(CMAKE_C_FLAGS "--std=gnu99 -fPIC -Wno-nullability-completeness ${CMAKE_C_FLAGS}")
else ()
set(CMAKE_C_FLAGS "--std=gnu99 ${CMAKE_C_FLAGS}")
endif ()
find_library(jpeg jpeg ${MOZJPEG_PATH}/lib)
find_library(turbojpeg turbojpeg ${MOZJPEG_PATH}/lib)
find_library(zopflipng zopflipng ${ZOPFLI_PATH}/lib)
add_library(caesium SHARED caesium.c error.c utils.c png.c vendor/lodepng.c jpeg.c)
target_link_libraries(caesium jpeg turbojpeg zopfli zopflipng)
# Make sure the compiler can find include files for our Caesium library
# when other libraries or executables link to Caesium
target_include_directories(caesium PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
install(FILES caesium.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include)
install(TARGETS caesium
LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)

View File

@ -1,74 +0,0 @@
#include <stdio.h>
#include "error.h"
#include "utils.h"
#include "png.h"
#include "jpeg.h"
bool cs_compress(const char *input_path, const char *output_path, cs_image_pars *options, int* err_n)
{
FILE *pInputFile;
image_type type;
bool result = false;
int compression_step_result;
if ((pInputFile = fopen(input_path, "rb")) == NULL) {
libcaesium_display_error(ERROR, 104);
*err_n = error_code;
return result;
}
type = detect_image_type(pInputFile);
fclose(pInputFile);
if (type == UNKN) {
libcaesium_display_error(WARNING, 103);
} else if (type == CS_JPEG) {
if (options->jpeg.quality != 0) {
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(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);
}
} else if (type == CS_PNG) {
result = cs_png_optimize(input_path, output_path, &options->png);
}
*err_n = error_code;
return result;
}
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 *options)
{
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()
{
cs_image_pars options;
initialize_jpeg_parameters(&options);
initialize_png_parameters(&options);
return options;
}

View File

@ -1,64 +0,0 @@
#ifndef LIBCAESIUM_CAESIUM_H
#define LIBCAESIUM_CAESIUM_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <string.h>
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
*/
int color_space;
int subsample;
int width;
int height;
} cs_jpeg_pars;
typedef struct cs_png_pars
{
int iterations;
int iterations_large;
int block_split_strategy;
bool lossy_8;
bool transparent;
int auto_filter_strategy;
double scale_factor;
} cs_png_pars;
typedef struct cs_image_pars
{
cs_jpeg_pars jpeg;
cs_png_pars png;
} cs_image_pars;
typedef enum image_type
{
CS_JPEG,
CS_PNG,
UNKN,
} image_type;
typedef enum error_level
{
ERROR = 0,
WARNING = 1
} error_level;
bool cs_compress(const char *input_path, const char *output_path, cs_image_pars *options, int* err_n);
cs_image_pars initialize_parameters();
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,5 +0,0 @@
#define VERSION_MAJOR @VERSION_MAJOR@
#define VERSION_MINOR @VERSION_MINOR@
#define VERSION_PATCH @VERSION_PATCH@
#define VERBOSE @VERBOSE@

View File

@ -1,67 +0,0 @@
#include <stdio.h>
#include "error.h"
#include "config.h"
int error_code = 0;
void libcaesium_display_error(error_level level, int code) {
error_code = code;
if (VERBOSE) {
char *error_level = ((level) ? "[WARNING]" : "[ERROR]");
fprintf(stderr, "%s %d: %s\n",
error_level,
code,
get_error_message(code));
}
}
const char *get_error_message(int code) {
switch (code) {
//Generic errors
case 101:
return "NULL file pointer while checking type.";
case 103:
return "File type not supported.";
case 104:
return "Could not open input file.";
//JPEG related errors
case 200:
return "Failed to open JPEG file while trying to get markers";
case 201:
return "Failed to open input JPEG file while optimizing";
case 202:
return "Failed to open output JPEG file while optimizing";
case 203:
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 "Decompressor failed";
case 209:
return "CMYK images are not fully supported and can only be optimized.";
//PNG related errors
case 300:
return "Failed to load PNG file.";
case 301:
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

@ -1,12 +0,0 @@
#ifndef LIBCAESIUM_ERROR_H
#define LIBCAESIUM_ERROR_H
#include "caesium.h"
extern int error_code;
void libcaesium_display_error(error_level level, int code);
const char *get_error_message(int code);
#endif

View File

@ -1,272 +0,0 @@
#include <stdio.h>
#include <jpeglib.h>
#include <string.h>
#include <turbojpeg.h>
#include <limits.h>
#include <math.h>
#include "jpeg.h"
#include "error.h"
#include "config.h"
METHODDEF(void)
do_not_emit_message () {}
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
if ((fp = fopen(input, "rb")) == NULL) {
libcaesium_display_error(ERROR, 200);
}
//Create the IO instance for the input file
jpeg_stdio_src(&einfo, fp);
//Save EXIF info
jpeg_save_markers(&einfo, JPEG_COM, 0xFFFF);
for (int m = 0; m < 16; m++) {
jpeg_save_markers(&einfo, JPEG_APP0 + m, 0xFFFF);
}
jpeg_read_header(&einfo, true);
fclose(fp);
return einfo;
}
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;
//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);
if (!VERBOSE) {
srcinfo.err->emit_message = do_not_emit_message;
dstinfo.err->emit_message = do_not_emit_message;
}
//Check for errors
if ((fp = fopen(input_file, "rb")) == NULL) {
libcaesium_display_error(ERROR, 201);
}
//Create the IO instance for the input file
jpeg_stdio_src(&srcinfo, fp);
//Save EXIF info
if (options->exif_copy) {
jpeg_save_markers(&srcinfo, JPEG_COM, 0xFFFF);
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);
//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
if ((fp = fopen(output_file, "wb")) == NULL) {
libcaesium_display_error(ERROR, 202);
}
//CRITICAL - This is the optimization step
dstinfo.optimize_coding = true;
//Set the output file parameters
jpeg_stdio_dest(&dstinfo, fp);
//Actually write the coefficients
jpeg_write_coefficients(&dstinfo, dst_coef_arrays);
//Write EXIF
if (options->exif_copy) {
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);
return true;
}
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;
unsigned long output_size = 0;
output_buffer = NULL;
int result = 0;
//Check for errors
if ((fp = fopen(output_file, "wb")) == NULL) {
libcaesium_display_error(ERROR, 203);
}
tjCompressHandle = tjInitCompress();
result = tjCompress2(tjCompressHandle,
image_buffer,
options->width,
0,
options->height,
options->color_space,
&output_buffer,
&output_size,
options->subsample,
options->quality,
options->dct_method);
if (result == -1) {
libcaesium_display_error(ERROR, 207);
} else {
fwrite(output_buffer, output_size, 1, fp);
}
fclose(fp);
tjDestroy(tjCompressHandle);
tjFree(output_buffer);
tjFree(image_buffer);
return 1;
}
unsigned char *cs_jpeg_decompress(const char *fileName, cs_jpeg_pars *options)
{
FILE *fp;
long sourceJpegBufferSize = 0;
unsigned char *sourceJpegBuffer = NULL;
tjhandle tjDecompressHandle;
int fileWidth = 0, fileHeight = 0, jpegSubsamp = 0, colorSpace = 0, result = 0;
if ((fp = fopen(fileName, "rb")) == NULL) {
libcaesium_display_error(ERROR, 204);
}
fseek(fp, 0, SEEK_END);
sourceJpegBufferSize = ftell(fp);
if (sourceJpegBufferSize == -1) {
libcaesium_display_error(ERROR, 205);
}
if (sourceJpegBufferSize > INT_MAX) {
libcaesium_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, (unsigned long) sourceJpegBufferSize, &fileWidth, &fileHeight,
&jpegSubsamp, &colorSpace);
if (colorSpace == 4) { //CMYK
libcaesium_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[colorSpace]);
result = tjDecompress2(tjDecompressHandle,
sourceJpegBuffer,
(unsigned long) sourceJpegBufferSize,
temp,
options->width,
0,
options->height,
colorSpace,
options->dct_method);
if (result == -1) {
libcaesium_display_error(ERROR, 208);
}
fclose(fp);
tjDestroy(tjDecompressHandle);
tjFree(sourceJpegBuffer);
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);
}
}

View File

@ -1,18 +0,0 @@
#ifndef LIBCAESIUM_JPEG_H
#define LIBCAESIUM_JPEG_H
#include <jpeglib.h>
#include "caesium.h"
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);
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);
void jcopy_markers_execute(j_decompress_ptr srcinfo, j_compress_ptr dstinfo);
#endif //LIBCAESIUM_JPEG_H

View File

@ -1,112 +0,0 @@
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STBIW_ASSERT(x)
#include <stdlib.h>
#include <zopflipng_lib.h>
#include "vendor/stb_image.h"
#include "vendor/stb_image_write.h"
#include "vendor/stb_image_resize.h"
#include "png.h"
#include "vendor/lodepng.h"
#include "error.h"
bool cs_png_optimize(const char *input, const char *output, cs_png_pars *options)
{
bool result = false;
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) {
libcaesium_display_error(ERROR, 304);
return result;
}
} else if (options->scale_factor != 1.0) {
libcaesium_display_error(ERROR, 305);
return false;
}
CZopfliPNGSetDefaults(&png_options);
unsigned char *orig_buffer;
size_t orig_buffer_size;
unsigned char *resultpng = NULL;
size_t resultpng_size;
png_options.num_iterations = options->iterations;
png_options.num_iterations_large = options->iterations_large;
png_options.block_split_strategy = options->block_split_strategy;
png_options.lossy_8bit = options->lossy_8;
png_options.lossy_transparent = options->transparent;
png_options.auto_filter_strategy = options->auto_filter_strategy;
if (lodepng_load_file(&orig_buffer, &orig_buffer_size, options->scale_factor == 1 ? input : output) != 0) {
error_code = 300;
goto cleanup;
}
int code = CZopfliPNGOptimize(orig_buffer,
orig_buffer_size,
&png_options,
0,
&resultpng,
&resultpng_size);
if (code != 0) {
error_code = 301;
goto cleanup;
}
if (lodepng_save_file(resultpng, resultpng_size, output) != 0) {
error_code = 302;
goto cleanup;
}
result = true;
cleanup:
free(orig_buffer);
free(resultpng);
if (error_code != 0) {
libcaesium_display_error(ERROR, error_code);
}
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,10 +0,0 @@
#ifndef LIBCAESIUM_PNG_H
#define LIBCAESIUM_PNG_H
#include "caesium.h"
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

View File

@ -1,27 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include "utils.h"
#include "error.h"
image_type detect_image_type(FILE *pFile)
{
unsigned char buffer[2];
if (pFile == NULL) {
libcaesium_display_error(WARNING, 101);
return UNKN;
}
if (fread(buffer, 1, 2, pFile) < 2) {
return UNKN;
}
if (buffer[0] == 0xFF && buffer[1] == 0xD8) {
return CS_JPEG;
} else if (buffer[0] == 0x89 && buffer[1] == 0x50) {
return CS_PNG;
}
return UNKN;
}

View File

@ -1,10 +0,0 @@
#ifndef LIBCAESIUM_UTILS_H
#define LIBCAESIUM_UTILS_H
#include <stdio.h>
#include "caesium.h"
image_type detect_image_type(FILE *pFile);
#endif

5926
caesium/vendor/lodepng.c vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,939 +0,0 @@
/*
LodePNG version 20131222
Copyright (c) 2005-2013 Lode Vandevenne
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
*/
#ifndef LODEPNG_H
#define LODEPNG_H
#include <string.h> /*for size_t*/
#ifdef __cplusplus
#include <vector>
#include <string>
#endif /*__cplusplus*/
/*
The following #defines are used to create code sections. They can be disabled
to disable code sections, which can give faster compile time and smaller binary.
The "NO_COMPILE" defines are designed to be used to pass as defines to the
compiler command to disable them without modifying this header, e.g.
-DLODEPNG_NO_COMPILE_ZLIB for gcc.
*/
/*deflate & zlib. If disabled, you must specify alternative zlib functions in
the custom_zlib field of the compress and decompress settings*/
#ifndef LODEPNG_NO_COMPILE_ZLIB
#define LODEPNG_COMPILE_ZLIB
#endif
/*png encoder and png decoder*/
#ifndef LODEPNG_NO_COMPILE_PNG
#define LODEPNG_COMPILE_PNG
#endif
/*deflate&zlib decoder and png decoder*/
#ifndef LODEPNG_NO_COMPILE_DECODER
#define LODEPNG_COMPILE_DECODER
#endif
/*deflate&zlib encoder and png encoder*/
#ifndef LODEPNG_NO_COMPILE_ENCODER
#define LODEPNG_COMPILE_ENCODER
#endif
/*the optional built in harddisk file loading and saving functions*/
#ifndef LODEPNG_NO_COMPILE_DISK
#define LODEPNG_COMPILE_DISK
#endif
/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/
#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
#define LODEPNG_COMPILE_ANCILLARY_CHUNKS
#endif
/*ability to convert error numerical codes to English text string*/
#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT
#define LODEPNG_COMPILE_ERROR_TEXT
#endif
/*Compile the default allocators (C's free, malloc and realloc). If you disable this,
you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your
source files with custom allocators.*/
#ifndef LODEPNG_NO_COMPILE_ALLOCATORS
#define LODEPNG_COMPILE_ALLOCATORS
#endif
/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/
#ifdef __cplusplus
#ifndef LODEPNG_NO_COMPILE_CPP
#define LODEPNG_COMPILE_CPP
#endif
#endif
#ifdef LODEPNG_COMPILE_PNG
/*The PNG color types (also used for raw).*/
typedef enum LodePNGColorType
{
LCT_GREY = 0, /*greyscale: 1,2,4,8,16 bit*/
LCT_RGB = 2, /*RGB: 8,16 bit*/
LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/
LCT_GREY_ALPHA = 4, /*greyscale with alpha: 8,16 bit*/
LCT_RGBA = 6 /*RGB with alpha: 8,16 bit*/
} LodePNGColorType;
#ifdef LODEPNG_COMPILE_DECODER
/*
Converts PNG data in memory to raw pixel data.
out: Output parameter. Pointer to buffer that will contain the raw pixel data.
After decoding, its size is w * h * (bytes per pixel) bytes larger than
initially. Bytes per pixel depends on colortype and bitdepth.
Must be freed after usage with free(*out).
Note: for 16-bit per channel colors, uses big endian format like PNG does.
w: Output parameter. Pointer to width of pixel data.
h: Output parameter. Pointer to height of pixel data.
in: Memory buffer with the PNG file.
insize: size of the in buffer.
colortype: the desired color type for the raw output image. See explanation on PNG color types.
bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types.
Return value: LodePNG error code (0 means no error).
*/
unsigned lodepng_decode_memory(unsigned char **out, unsigned *w, unsigned *h,
const unsigned char *in, size_t insize,
LodePNGColorType colortype, unsigned bitdepth);
/*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/
unsigned lodepng_decode32(unsigned char **out, unsigned *w, unsigned *h,
const unsigned char *in, size_t insize);
/*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/
unsigned lodepng_decode24(unsigned char **out, unsigned *w, unsigned *h,
const unsigned char *in, size_t insize);
#ifdef LODEPNG_COMPILE_DISK
/*
Load PNG from disk, from file with given name.
Same as the other decode functions, but instead takes a filename as input.
*/
unsigned lodepng_decode_file(unsigned char **out, unsigned *w, unsigned *h,
const char *filename,
LodePNGColorType colortype, unsigned bitdepth);
/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/
unsigned lodepng_decode32_file(unsigned char **out, unsigned *w, unsigned *h,
const char *filename);
/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/
unsigned lodepng_decode24_file(unsigned char **out, unsigned *w, unsigned *h,
const char *filename);
#endif /*LODEPNG_COMPILE_DISK*/
#endif /*LODEPNG_COMPILE_DECODER*/
#ifdef LODEPNG_COMPILE_ENCODER
/*
Converts raw pixel data into a PNG image in memory. The colortype and bitdepth
of the output PNG image cannot be chosen, they are automatically determined
by the colortype, bitdepth and content of the input pixel data.
Note: for 16-bit per channel colors, needs big endian format like PNG does.
out: Output parameter. Pointer to buffer that will contain the PNG image data.
Must be freed after usage with free(*out).
outsize: Output parameter. Pointer to the size in bytes of the out buffer.
image: The raw pixel data to encode. The size of this buffer should be
w * h * (bytes per pixel), bytes per pixel depends on colortype and bitdepth.
w: width of the raw pixel data in pixels.
h: height of the raw pixel data in pixels.
colortype: the color type of the raw input image. See explanation on PNG color types.
bitdepth: the bit depth of the raw input image. See explanation on PNG color types.
Return value: LodePNG error code (0 means no error).
*/
unsigned lodepng_encode_memory(unsigned char **out, size_t *outsize,
const unsigned char *image, unsigned w, unsigned h,
LodePNGColorType colortype, unsigned bitdepth);
/*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/
unsigned lodepng_encode32(unsigned char **out, size_t *outsize,
const unsigned char *image, unsigned w, unsigned h);
/*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/
unsigned lodepng_encode24(unsigned char **out, size_t *outsize,
const unsigned char *image, unsigned w, unsigned h);
#ifdef LODEPNG_COMPILE_DISK
/*
Converts raw pixel data into a PNG file on disk.
Same as the other encode functions, but instead takes a filename as output.
NOTE: This overwrites existing files without warning!
*/
unsigned lodepng_encode_file(const char *filename,
const unsigned char *image, unsigned w, unsigned h,
LodePNGColorType colortype, unsigned bitdepth);
/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/
unsigned lodepng_encode32_file(const char *filename,
const unsigned char *image, unsigned w, unsigned h);
/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/
unsigned lodepng_encode24_file(const char *filename,
const unsigned char *image, unsigned w, unsigned h);
#endif /*LODEPNG_COMPILE_DISK*/
#endif /*LODEPNG_COMPILE_ENCODER*/
#ifdef LODEPNG_COMPILE_CPP
namespace lodepng
{
#ifdef LODEPNG_COMPILE_DECODER
/*Same as lodepng_decode_memory, but decodes to an std::vector.*/
unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
const unsigned char* in, size_t insize,
LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
const std::vector<unsigned char>& in,
LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
#ifdef LODEPNG_COMPILE_DISK
/*
Converts PNG file from disk to raw pixel data in memory.
Same as the other decode functions, but instead takes a filename as input.
*/
unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
const std::string& filename,
LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
#endif //LODEPNG_COMPILE_DISK
#endif //LODEPNG_COMPILE_DECODER
#ifdef LODEPNG_COMPILE_ENCODER
/*Same as lodepng_encode_memory, but encodes to an std::vector.*/
unsigned encode(std::vector<unsigned char>& out,
const unsigned char* in, unsigned w, unsigned h,
LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
unsigned encode(std::vector<unsigned char>& out,
const std::vector<unsigned char>& in, unsigned w, unsigned h,
LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
#ifdef LODEPNG_COMPILE_DISK
/*
Converts 32-bit RGBA raw pixel data into a PNG file on disk.
Same as the other encode functions, but instead takes a filename as output.
NOTE: This overwrites existing files without warning!
*/
unsigned encode(const std::string& filename,
const unsigned char* in, unsigned w, unsigned h,
LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
unsigned encode(const std::string& filename,
const std::vector<unsigned char>& in, unsigned w, unsigned h,
LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
#endif //LODEPNG_COMPILE_DISK
#endif //LODEPNG_COMPILE_ENCODER
} //namespace lodepng
#endif /*LODEPNG_COMPILE_CPP*/
#endif /*LODEPNG_COMPILE_PNG*/
#ifdef LODEPNG_COMPILE_ERROR_TEXT
/*Returns an English description of the numerical error code.*/
const char *lodepng_error_text(unsigned code);
#endif /*LODEPNG_COMPILE_ERROR_TEXT*/
#ifdef LODEPNG_COMPILE_DECODER
/*Settings for zlib decompression*/
typedef struct LodePNGDecompressSettings LodePNGDecompressSettings;
struct LodePNGDecompressSettings
{
unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/
/*use custom zlib decoder instead of built in one (default: null)*/
unsigned (*custom_zlib)(unsigned char **, size_t *,
const unsigned char *, size_t,
const LodePNGDecompressSettings *);
/*use custom deflate decoder instead of built in one (default: null)
if custom_zlib is used, custom_deflate is ignored since only the built in
zlib function will call custom_deflate*/
unsigned (*custom_inflate)(unsigned char **, size_t *,
const unsigned char *, size_t,
const LodePNGDecompressSettings *);
const void *custom_context; /*optional custom settings for custom functions*/
};
extern const LodePNGDecompressSettings lodepng_default_decompress_settings;
void lodepng_decompress_settings_init(LodePNGDecompressSettings *settings);
#endif /*LODEPNG_COMPILE_DECODER*/
#ifdef LODEPNG_COMPILE_ENCODER
/*
Settings for zlib compression. Tweaking these settings tweaks the balance
between speed and compression ratio.
*/
typedef struct LodePNGCompressSettings LodePNGCompressSettings;
struct LodePNGCompressSettings /*deflate = compress*/
{
/*LZ77 related settings*/
unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/
unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/
unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Typical value: 2048.*/
unsigned minmatch; /*mininum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/
unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/
unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/
/*use custom zlib encoder instead of built in one (default: null)*/
unsigned (*custom_zlib)(unsigned char **, size_t *,
const unsigned char *, size_t,
const LodePNGCompressSettings *);
/*use custom deflate encoder instead of built in one (default: null)
if custom_zlib is used, custom_deflate is ignored since only the built in
zlib function will call custom_deflate*/
unsigned (*custom_deflate)(unsigned char **, size_t *,
const unsigned char *, size_t,
const LodePNGCompressSettings *);
const void *custom_context; /*optional custom settings for custom functions*/
};
extern const LodePNGCompressSettings lodepng_default_compress_settings;
void lodepng_compress_settings_init(LodePNGCompressSettings *settings);
#endif /*LODEPNG_COMPILE_ENCODER*/
#ifdef LODEPNG_COMPILE_PNG
/*
Color mode of an image. Contains all information required to decode the pixel
bits to RGBA colors. This information is the same as used in the PNG file
format, and is used both for PNG and raw image data in LodePNG.
*/
typedef struct LodePNGColorMode
{
/*header (IHDR)*/
LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/
unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/
/*
palette (PLTE and tRNS)
Dynamically allocated with the colors of the palette, including alpha.
When encoding a PNG, to store your colors in the palette of the LodePNGColorMode, first use
lodepng_palette_clear, then for each color use lodepng_palette_add.
If you encode an image without alpha with palette, don't forget to put value 255 in each A byte of the palette.
When decoding, by default you can ignore this palette, since LodePNG already
fills the palette colors in the pixels of the raw RGBA output.
The palette is only supported for color type 3.
*/
unsigned char *palette; /*palette in RGBARGBA... order. When allocated, must be either 0, or have size 1024*/
size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/
/*
transparent color key (tRNS)
This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit.
For greyscale PNGs, r, g and b will all 3 be set to the same.
When decoding, by default you can ignore this information, since LodePNG sets
pixels with this key to transparent already in the raw RGBA output.
The color key is only supported for color types 0 and 2.
*/
unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/
unsigned key_r; /*red/greyscale component of color key*/
unsigned key_g; /*green component of color key*/
unsigned key_b; /*blue component of color key*/
} LodePNGColorMode;
/*init, cleanup and copy functions to use with this struct*/
void lodepng_color_mode_init(LodePNGColorMode *info);
void lodepng_color_mode_cleanup(LodePNGColorMode *info);
/*return value is error code (0 means no error)*/
unsigned lodepng_color_mode_copy(LodePNGColorMode *dest, const LodePNGColorMode *source);
void lodepng_palette_clear(LodePNGColorMode *info);
/*add 1 color to the palette*/
unsigned lodepng_palette_add(LodePNGColorMode *info,
unsigned char r, unsigned char g, unsigned char b, unsigned char a);
/*get the total amount of bits per pixel, based on colortype and bitdepth in the struct*/
unsigned lodepng_get_bpp(const LodePNGColorMode *info);
/*get the amount of color channels used, based on colortype in the struct.
If a palette is used, it counts as 1 channel.*/
unsigned lodepng_get_channels(const LodePNGColorMode *info);
/*is it a greyscale type? (only colortype 0 or 4)*/
unsigned lodepng_is_greyscale_type(const LodePNGColorMode *info);
/*has it got an alpha channel? (only colortype 2 or 6)*/
unsigned lodepng_is_alpha_type(const LodePNGColorMode *info);
/*has it got a palette? (only colortype 3)*/
unsigned lodepng_is_palette_type(const LodePNGColorMode *info);
/*only returns true if there is a palette and there is a value in the palette with alpha < 255.
Loops through the palette to check this.*/
unsigned lodepng_has_palette_alpha(const LodePNGColorMode *info);
/*
Check if the given color info indicates the possibility of having non-opaque pixels in the PNG image.
Returns true if the image can have translucent or invisible pixels (it still be opaque if it doesn't use such pixels).
Returns false if the image can only have opaque pixels.
In detail, it returns true only if it's a color type with alpha, or has a palette with non-opaque values,
or if "key_defined" is true.
*/
unsigned lodepng_can_have_alpha(const LodePNGColorMode *info);
/*Returns the byte size of a raw image buffer with given width, height and color mode*/
size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode *color);
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
/*The information of a Time chunk in PNG.*/
typedef struct LodePNGTime
{
unsigned year; /*2 bytes used (0-65535)*/
unsigned month; /*1-12*/
unsigned day; /*1-31*/
unsigned hour; /*0-23*/
unsigned minute; /*0-59*/
unsigned second; /*0-60 (to allow for leap seconds)*/
} LodePNGTime;
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
/*Information about the PNG image, except pixels, width and height.*/
typedef struct LodePNGInfo
{
/*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/
unsigned compression_method;/*compression method of the original file. Always 0.*/
unsigned filter_method; /*filter method of the original file*/
unsigned interlace_method; /*interlace method of the original file*/
LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
/*
suggested background color chunk (bKGD)
This color uses the same color mode as the PNG (except alpha channel), which can be 1-bit to 16-bit.
For greyscale PNGs, r, g and b will all 3 be set to the same. When encoding
the encoder writes the red one. For palette PNGs: When decoding, the RGB value
will be stored, not a palette index. But when encoding, specify the index of
the palette in background_r, the other two are then ignored.
The decoder does not use this background color to edit the color of pixels.
*/
unsigned background_defined; /*is a suggested background color given?*/
unsigned background_r; /*red component of suggested background color*/
unsigned background_g; /*green component of suggested background color*/
unsigned background_b; /*blue component of suggested background color*/
/*
non-international text chunks (tEXt and zTXt)
The char** arrays each contain num strings. The actual messages are in
text_strings, while text_keys are keywords that give a short description what
the actual text represents, e.g. Title, Author, Description, or anything else.
A keyword is minimum 1 character and maximum 79 characters long. It's
discouraged to use a single line length longer than 79 characters for texts.
Don't allocate these text buffers yourself. Use the init/cleanup functions
correctly and use lodepng_add_text and lodepng_clear_text.
*/
size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/
char **text_keys; /*the keyword of a text chunk (e.g. "Comment")*/
char **text_strings; /*the actual text*/
/*
international text chunks (iTXt)
Similar to the non-international text chunks, but with additional strings
"langtags" and "transkeys".
*/
size_t itext_num; /*the amount of international texts in this PNG*/
char **itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/
char **itext_langtags; /*language tag for this text's language, ISO/IEC 646 string, e.g. ISO 639 language tag*/
char **itext_transkeys; /*keyword translated to the international language - UTF-8 string*/
char **itext_strings; /*the actual international text - UTF-8 string*/
/*time chunk (tIME)*/
unsigned time_defined; /*set to 1 to make the encoder generate a tIME chunk*/
LodePNGTime time;
/*phys chunk (pHYs)*/
unsigned phys_defined; /*if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one*/
unsigned phys_x; /*pixels per unit in x direction*/
unsigned phys_y; /*pixels per unit in y direction*/
unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/
/*
unknown chunks
There are 3 buffers, one for each position in the PNG where unknown chunks can appear
each buffer contains all unknown chunks for that position consecutively
The 3 buffers are the unknown chunks between certain critical chunks:
0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND
Do not allocate or traverse this data yourself. Use the chunk traversing functions declared
later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct.
*/
unsigned char *unknown_chunks_data[3];
size_t unknown_chunks_size[3]; /*size in bytes of the unknown chunks, given for protection*/
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
} LodePNGInfo;
/*init, cleanup and copy functions to use with this struct*/
void lodepng_info_init(LodePNGInfo *info);
void lodepng_info_cleanup(LodePNGInfo *info);
/*return value is error code (0 means no error)*/
unsigned lodepng_info_copy(LodePNGInfo *dest, const LodePNGInfo *source);
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
void lodepng_clear_text(LodePNGInfo *info); /*use this to clear the texts again after you filled them in*/
unsigned lodepng_add_text(LodePNGInfo *info, const char *key, const char *str); /*push back both texts at once*/
void lodepng_clear_itext(LodePNGInfo *info); /*use this to clear the itexts again after you filled them in*/
unsigned lodepng_add_itext(LodePNGInfo *info, const char *key, const char *langtag,
const char *transkey, const char *str); /*push back the 4 texts of 1 chunk at once*/
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
/*
Converts raw buffer from one color type to another color type, based on
LodePNGColorMode structs to describe the input and output color type.
See the reference manual at the end of this header file to see which color conversions are supported.
return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported)
The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel
of the output color type (lodepng_get_bpp)
The fix_png value works as described in struct LodePNGDecoderSettings.
Note: for 16-bit per channel colors, uses big endian format like PNG does.
*/
unsigned lodepng_convert(unsigned char *out, const unsigned char *in,
LodePNGColorMode *mode_out, const LodePNGColorMode *mode_in,
unsigned w, unsigned h, unsigned fix_png);
#ifdef LODEPNG_COMPILE_DECODER
/*
Settings for the decoder. This contains settings for the PNG and the Zlib
decoder, but not the Info settings from the Info structs.
*/
typedef struct LodePNGDecoderSettings
{
LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/
unsigned ignore_crc; /*ignore CRC checksums*/
/*
The fix_png setting, if 1, makes the decoder tolerant towards some PNG images
that do not correctly follow the PNG specification. This only supports errors
that are fixable, were found in images that are actually used on the web, and
are silently tolerated by other decoders as well. Currently only one such fix
is implemented: if a palette index is out of bounds given the palette size,
interpret it as opaque black.
By default this value is 0, which makes it stop with an error on such images.
*/
unsigned fix_png;
unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/
/*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/
unsigned remember_unknown_chunks;
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
} LodePNGDecoderSettings;
void lodepng_decoder_settings_init(LodePNGDecoderSettings *settings);
#endif /*LODEPNG_COMPILE_DECODER*/
#ifdef LODEPNG_COMPILE_ENCODER
/*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/
typedef enum LodePNGFilterStrategy
{
/*every filter at zero*/
LFS_ZERO,
/*Use filter that gives minumum sum, as described in the official PNG filter heuristic.*/
LFS_MINSUM,
/*Use the filter type that gives smallest Shannon entropy for this scanline. Depending
on the image, this is better or worse than minsum.*/
LFS_ENTROPY,
/*
Brute-force-search PNG filters by compressing each filter for each scanline.
Experimental, very slow, and only rarely gives better compression than MINSUM.
*/
LFS_BRUTE_FORCE,
/*use predefined_filters buffer: you specify the filter type for each scanline*/
LFS_PREDEFINED
} LodePNGFilterStrategy;
/*automatically use color type with less bits per pixel if losslessly possible. Default: LAC_AUTO*/
typedef enum LodePNGAutoConvert
{
LAC_NO, /*use color type user requested*/
LAC_ALPHA, /*use color type user requested, but if only opaque pixels and RGBA or grey+alpha, use RGB or grey*/
LAC_AUTO, /*use PNG color type that can losslessly represent the uncompressed image the smallest possible*/
/*
like AUTO, but do not choose 1, 2 or 4 bit per pixel types.
sometimes a PNG image compresses worse if less than 8 bits per pixels.
*/
LAC_AUTO_NO_NIBBLES,
/*
like AUTO, but never choose palette color type. For small images, encoding
the palette may take more bytes than what is gained. Note that AUTO also
already prevents encoding the palette for extremely small images, but that may
not be sufficient because due to the compression it cannot predict when to
switch.
*/
LAC_AUTO_NO_PALETTE,
LAC_AUTO_NO_NIBBLES_NO_PALETTE
} LodePNGAutoConvert;
/*
Automatically chooses color type that gives smallest amount of bits in the
output image, e.g. grey if there are only greyscale pixels, palette if there
are less than 256 colors, ...
The auto_convert parameter allows limiting it to not use palette, ...
*/
unsigned lodepng_auto_choose_color(LodePNGColorMode *mode_out,
const unsigned char *image, unsigned w, unsigned h,
const LodePNGColorMode *mode_in,
LodePNGAutoConvert auto_convert);
/*Settings for the encoder.*/
typedef struct LodePNGEncoderSettings
{
LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/
LodePNGAutoConvert auto_convert; /*how to automatically choose output PNG color type, if at all*/
/*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than
8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to
completely follow the official PNG heuristic, filter_palette_zero must be true and
filter_strategy must be LFS_MINSUM*/
unsigned filter_palette_zero;
/*Which filter strategy to use when not using zeroes due to filter_palette_zero.
Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: LFS_MINSUM*/
LodePNGFilterStrategy filter_strategy;
/*used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with
the same length as the amount of scanlines in the image, and each value must <= 5. You
have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero
must be set to 0 to ensure this is also used on palette or low bitdepth images.*/
const unsigned char *predefined_filters;
/*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette).
If colortype is 3, PLTE is _always_ created.*/
unsigned force_palette;
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
/*add LodePNG identifier and version as a text chunk, for debugging*/
unsigned add_id;
/*encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks*/
unsigned text_compression;
#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
} LodePNGEncoderSettings;
void lodepng_encoder_settings_init(LodePNGEncoderSettings *settings);
#endif /*LODEPNG_COMPILE_ENCODER*/
#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER)
/*The settings, state and information for extended encoding and decoding.*/
typedef struct LodePNGState
{
#ifdef LODEPNG_COMPILE_DECODER
LodePNGDecoderSettings decoder; /*the decoding settings*/
#endif /*LODEPNG_COMPILE_DECODER*/
#ifdef LODEPNG_COMPILE_ENCODER
LodePNGEncoderSettings encoder; /*the encoding settings*/
#endif /*LODEPNG_COMPILE_ENCODER*/
LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/
LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/
unsigned error;
#ifdef LODEPNG_COMPILE_CPP
//For the lodepng::State subclass.
virtual ~LodePNGState(){}
#endif
} LodePNGState;
/*init, cleanup and copy functions to use with this struct*/
void lodepng_state_init(LodePNGState *state);
void lodepng_state_cleanup(LodePNGState *state);
void lodepng_state_copy(LodePNGState *dest, const LodePNGState *source);
#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */
#ifdef LODEPNG_COMPILE_DECODER
/*
Same as lodepng_decode_memory, but uses a LodePNGState to allow custom settings and
getting much more information about the PNG image and color mode.
*/
unsigned lodepng_decode(unsigned char **out, unsigned *w, unsigned *h,
LodePNGState *state,
const unsigned char *in, size_t insize);
/*
Read the PNG header, but not the actual data. This returns only the information
that is in the header chunk of the PNG, such as width, height and color type. The
information is placed in the info_png field of the LodePNGState.
*/
unsigned lodepng_inspect(unsigned *w, unsigned *h,
LodePNGState *state,
const unsigned char *in, size_t insize);
#endif /*LODEPNG_COMPILE_DECODER*/
#ifdef LODEPNG_COMPILE_ENCODER
/*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/
unsigned lodepng_encode(unsigned char **out, size_t *outsize,
const unsigned char *image, unsigned w, unsigned h,
LodePNGState *state);
#endif /*LODEPNG_COMPILE_ENCODER*/
/*
The lodepng_chunk functions are normally not needed, except to traverse the
unknown chunks stored in the LodePNGInfo struct, or add new ones to it.
It also allows traversing the chunks of an encoded PNG file yourself.
PNG standard chunk naming conventions:
First byte: uppercase = critical, lowercase = ancillary
Second byte: uppercase = public, lowercase = private
Third byte: must be uppercase
Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy
*/
/*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/
unsigned lodepng_chunk_length(const unsigned char *chunk);
/*puts the 4-byte type in null terminated string*/
void lodepng_chunk_type(char type[5], const unsigned char *chunk);
/*check if the type is the given type*/
unsigned char lodepng_chunk_type_equals(const unsigned char *chunk, const char *type);
/*0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard)*/
unsigned char lodepng_chunk_ancillary(const unsigned char *chunk);
/*0: public, 1: private (see PNG standard)*/
unsigned char lodepng_chunk_private(const unsigned char *chunk);
/*0: the chunk is unsafe to copy, 1: the chunk is safe to copy (see PNG standard)*/
unsigned char lodepng_chunk_safetocopy(const unsigned char *chunk);
/*get pointer to the data of the chunk, where the input points to the header of the chunk*/
unsigned char *lodepng_chunk_data(unsigned char *chunk);
const unsigned char *lodepng_chunk_data_const(const unsigned char *chunk);
/*returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!)*/
unsigned lodepng_chunk_check_crc(const unsigned char *chunk);
/*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/
void lodepng_chunk_generate_crc(unsigned char *chunk);
/*iterate to next chunks. don't use on IEND chunk, as there is no next chunk then*/
unsigned char *lodepng_chunk_next(unsigned char *chunk);
const unsigned char *lodepng_chunk_next_const(const unsigned char *chunk);
/*
Appends chunk to the data in out. The given chunk should already have its chunk header.
The out variable and outlength are updated to reflect the new reallocated buffer.
Returns error code (0 if it went ok)
*/
unsigned lodepng_chunk_append(unsigned char **out, size_t *outlength, const unsigned char *chunk);
/*
Appends new chunk to out. The chunk to append is given by giving its length, type
and data separately. The type is a 4-letter string.
The out variable and outlength are updated to reflect the new reallocated buffer.
Returne error code (0 if it went ok)
*/
unsigned lodepng_chunk_create(unsigned char **out, size_t *outlength, unsigned length,
const char *type, const unsigned char *data);
/*Calculate CRC32 of buffer*/
unsigned lodepng_crc32(const unsigned char *buf, size_t len);
#endif /*LODEPNG_COMPILE_PNG*/
#ifdef LODEPNG_COMPILE_ZLIB
/*
This zlib part can be used independently to zlib compress and decompress a
buffer. It cannot be used to create gzip files however, and it only supports the
part of zlib that is required for PNG, it does not support dictionaries.
*/
#ifdef LODEPNG_COMPILE_DECODER
/*Inflate a buffer. Inflate is the decompression step of deflate. Out buffer must be freed after use.*/
unsigned lodepng_inflate(unsigned char **out, size_t *outsize,
const unsigned char *in, size_t insize,
const LodePNGDecompressSettings *settings);
/*
Decompresses Zlib data. Reallocates the out buffer and appends the data. The
data must be according to the zlib specification.
Either, *out must be NULL and *outsize must be 0, or, *out must be a valid
buffer and *outsize its size in bytes. out must be freed by user after usage.
*/
unsigned lodepng_zlib_decompress(unsigned char **out, size_t *outsize,
const unsigned char *in, size_t insize,
const LodePNGDecompressSettings *settings);
#endif /*LODEPNG_COMPILE_DECODER*/
#ifdef LODEPNG_COMPILE_ENCODER
/*
Compresses data with Zlib. Reallocates the out buffer and appends the data.
Zlib adds a small header and trailer around the deflate data.
The data is output in the format of the zlib specification.
Either, *out must be NULL and *outsize must be 0, or, *out must be a valid
buffer and *outsize its size in bytes. out must be freed by user after usage.
*/
unsigned lodepng_zlib_compress(unsigned char **out, size_t *outsize,
const unsigned char *in, size_t insize,
const LodePNGCompressSettings *settings);
/*
Find length-limited Huffman code for given frequencies. This function is in the
public interface only for tests, it's used internally by lodepng_deflate.
*/
unsigned lodepng_huffman_code_lengths(unsigned *lengths, const unsigned *frequencies,
size_t numcodes, unsigned maxbitlen);
/*Compress a buffer with deflate. See RFC 1951. Out buffer must be freed after use.*/
unsigned lodepng_deflate(unsigned char **out, size_t *outsize,
const unsigned char *in, size_t insize,
const LodePNGCompressSettings *settings);
#endif /*LODEPNG_COMPILE_ENCODER*/
#endif /*LODEPNG_COMPILE_ZLIB*/
#ifdef LODEPNG_COMPILE_DISK
/*
Load a file from disk into buffer. The function allocates the out buffer, and
after usage you should free it.
out: output parameter, contains pointer to loaded buffer.
outsize: output parameter, size of the allocated out buffer
filename: the path to the file to load
return value: error code (0 means ok)
*/
unsigned lodepng_load_file(unsigned char **out, size_t *outsize, const char *filename);
/*
Save a file from buffer to disk. Warning, if it exists, this function overwrites
the file without warning!
buffer: the buffer to write
buffersize: size of the buffer to write
filename: the path to the file to save to
return value: error code (0 means ok)
*/
unsigned lodepng_save_file(const unsigned char *buffer, size_t buffersize, const char *filename);
#endif /*LODEPNG_COMPILE_DISK*/
#ifdef LODEPNG_COMPILE_CPP
//The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers.
namespace lodepng
{
#ifdef LODEPNG_COMPILE_PNG
class State : public LodePNGState
{
public:
State();
State(const State& other);
virtual ~State();
State& operator=(const State& other);
};
#ifdef LODEPNG_COMPILE_DECODER
//Same as other lodepng::decode, but using a State for more settings and information.
unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
State& state,
const unsigned char* in, size_t insize);
unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
State& state,
const std::vector<unsigned char>& in);
#endif /*LODEPNG_COMPILE_DECODER*/
#ifdef LODEPNG_COMPILE_ENCODER
//Same as other lodepng::encode, but using a State for more settings and information.
unsigned encode(std::vector<unsigned char>& out,
const unsigned char* in, unsigned w, unsigned h,
State& state);
unsigned encode(std::vector<unsigned char>& out,
const std::vector<unsigned char>& in, unsigned w, unsigned h,
State& state);
#endif /*LODEPNG_COMPILE_ENCODER*/
#ifdef LODEPNG_COMPILE_DISK
/*
Load a file from disk into an std::vector. If the vector is empty, then either
the file doesn't exist or is an empty file.
*/
void load_file(std::vector<unsigned char>& buffer, const std::string& filename);
/*
Save the binary data in an std::vector to a file on disk. The file is overwritten
without warning.
*/
void save_file(const std::vector<unsigned char>& buffer, const std::string& filename);
#endif //LODEPNG_COMPILE_DISK
#endif //LODEPNG_COMPILE_PNG
#ifdef LODEPNG_COMPILE_ZLIB
#ifdef LODEPNG_COMPILE_DECODER
//Zlib-decompress an unsigned char buffer
unsigned decompress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings);
//Zlib-decompress an std::vector
unsigned decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings);
#endif //LODEPNG_COMPILE_DECODER
#ifdef LODEPNG_COMPILE_ENCODER
//Zlib-compress an unsigned char buffer
unsigned compress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
const LodePNGCompressSettings& settings = lodepng_default_compress_settings);
//Zlib-compress an std::vector
unsigned compress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
const LodePNGCompressSettings& settings = lodepng_default_compress_settings);
#endif //LODEPNG_COMPILE_ENCODER
#endif //LODEPNG_COMPILE_ZLIB
} //namespace lodepng
#endif /*LODEPNG_COMPILE_CPP*/
#endif /*LODEPNG_H inclusion guard*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +0,0 @@
if (NOT WIN32)
set(CMAKE_C_FLAGS "--std=gnu99 -fPIC -Wno-nullability-completeness ${CMAKE_C_FLAGS}")
else ()
set(CMAKE_C_FLAGS "--std=gnu99 ${CMAKE_C_FLAGS}")
endif ()
add_executable(caesiumd main.c)
target_link_libraries(caesiumd LINK_PUBLIC caesium)

View File

@ -1,24 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../caesium/caesium.h"
#include "config.h"
int main(int argc, char *argv[])
{
if (argc == 2 && strcmp(argv[1], "-v") == 0) {
fprintf(stdout, "%d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
exit(EXIT_SUCCESS);
}
if (argc != 3) {
fprintf(stderr, "Wrong arguments.\nExiting.\n");
exit(EXIT_FAILURE);
}
cs_image_pars options = initialize_parameters();
int error_code = 0;
cs_compress(argv[1], argv[2], &options, &error_code);
exit(error_code);
}

View File

@ -1,29 +0,0 @@
#!/bin/bash
SOURCE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
rm -rf ./mozjpeg
rm -rf ./zopfli
#mozjpeg
git clone https://github.com/mozilla/mozjpeg.git
cd mozjpeg/ || exit
git checkout 426de82d0c081c996c23b75fed05833b6627b590
mkdir build && cd build || exit
cmake -G"Unix Makefiles" ..
make && sudo make install
cd "${SOURCE}" || exit
#zopflipng
git clone --branch 'zopfli-1.0.3' --depth 1 https://github.com/google/zopfli.git
cd zopfli || exit
mkdir build && cd build || exit
if [[ "$OSTYPE" == "linux-gnu" ]]; then
cmake -D"ZOPFLI_BUILD_SHARED=ON" -D"CMAKE_INSTALL_BINDIR=/usr/bin" -D"CMAKE_INSTALL_LIBDIR=/usr/lib" -D"CMAKE_INSTALL_INCLUDEDIR=/usr/include" ..
else
cmake -D"ZOPFLI_BUILD_SHARED=ON" ..
fi
make libzopflipng && sudo make install
cd "${SOURCE}" || exit

37
src/gif.rs Normal file
View File

@ -0,0 +1,37 @@
use std::ffi::CString;
use std::io;
use std::os::raw::{c_int, c_void};
use crate::CSParameters;
pub struct Parameters {
pub quality: u32,
}
pub fn compress(input_path: String, output_path: String, parameters: CSParameters) -> Result<(), io::Error>
{
unsafe {
let input_file = libc::fopen(CString::new(input_path)?.as_ptr(), CString::new("r")?.as_ptr());
let output_file = libc::fopen(CString::new(output_path)?.as_ptr(), CString::new("w+")?.as_ptr());
let input_stream = gifsicle::Gif_ReadFile(input_file);
libc::fclose(input_file);
let padding: [*mut c_void; 7] = [std::ptr::null_mut(); 7];
let mut loss = 0;
if !parameters.optimize {
loss = (100 - parameters.gif.quality) as c_int
}
let gc_info = gifsicle::Gif_CompressInfo {
flags: 0,
loss,
padding,
};
let write_result = gifsicle::Gif_FullWriteFile(input_stream, &gc_info, output_file);
libc::fclose(output_file);
match write_result {
1 => Ok(()),
_ => Err(io::Error::new(io::ErrorKind::Other, "GIF compression failed!"))
}
}
}

133
src/jpeg.rs Normal file
View File

@ -0,0 +1,133 @@
use mozjpeg::{Decompress, Compress, ALL_MARKERS, NO_MARKERS};
use std::fs::File;
use std::io::Write;
use std::{io, mem};
use mozjpeg_sys as mjs;
use crate::CSParameters;
use std::fs;
pub struct Parameters {
pub quality: u32,
}
pub fn compress(input_path: String, output_path: String, parameters: CSParameters) -> Result<(), io::Error>
{
return if parameters.optimize {
unsafe {
lossless(input_path, output_path, parameters)
}
} else {
lossy(input_path, output_path, parameters)
}
}
unsafe fn lossless(input_path: String, output_path: String, parameters: CSParameters) -> Result<(), io::Error>
{
let mut src_info: mjs::jpeg_decompress_struct = mem::zeroed();
let mut src_err = mem::zeroed();
let mut dst_info: mjs::jpeg_compress_struct = mem::zeroed();
let mut dst_err = mem::zeroed();
src_info.common.err = mjs::jpeg_std_error(&mut src_err);
dst_info.common.err = mjs::jpeg_std_error(&mut dst_err);
mjs::jpeg_create_decompress(&mut src_info);
mjs::jpeg_create_compress(&mut dst_info);
let in_file = fs::read(input_path)?;
mjs::jpeg_mem_src(&mut src_info, in_file.as_ptr(), in_file.len() as _);
if parameters.keep_metadata {
mjs::jpeg_save_markers(&mut src_info, 0xFE, 0xFFFF);
for m in 0..16 {
mjs::jpeg_save_markers(&mut src_info, 0xE0 + m, 0xFFFF);
}
}
mjs::jpeg_read_header(&mut src_info, i32::from(true));
let src_coef_arrays = mjs::jpeg_read_coefficients(&mut src_info);
mjs::jpeg_copy_critical_parameters(&src_info, &mut dst_info);
let dst_coef_arrays = src_coef_arrays;
dst_info.optimize_coding = i32::from(true);
let mut buf = std::ptr::null_mut();
let mut buf_size = 0;
mjs::jpeg_mem_dest(&mut dst_info, &mut buf, &mut buf_size);
mjs::jpeg_write_coefficients(&mut dst_info, dst_coef_arrays);
if parameters.keep_metadata {
let mut marker = src_info.marker_list;
while !marker.is_null() {
mjs::jpeg_write_marker(&mut dst_info, (*marker).marker as i32, (*marker).data, (*marker).data_length);
marker = (*marker).next;
}
}
mjs::jpeg_finish_compress(&mut dst_info);
mjs::jpeg_destroy_compress(&mut dst_info);
mjs::jpeg_finish_decompress(&mut src_info);
mjs::jpeg_destroy_decompress(&mut src_info);
let mut output_file_buffer = File::create(output_path)?;
output_file_buffer.write_all(std::slice::from_raw_parts(buf, buf_size as usize))?;
Ok(())
}
fn lossy(input_path: String, output_path: String, parameters: CSParameters) -> Result<(), io::Error> {
let markers_option = if parameters.keep_metadata { ALL_MARKERS } else { NO_MARKERS };
let data = std::fs::read(input_path).unwrap();
let mem_data = &data[..data.len()];
let decompress = Decompress::with_markers(markers_option).from_mem(mem_data)?;
let samp_factors = decompress.components()
.iter()
.map(|c| c.v_samp_factor)
.collect::<Vec<_>>();
let markers = decompress.markers();
let color_space = decompress.color_space();
let width = decompress.width();
let height = decompress.height();
let mut c_info = Compress::new(color_space);
c_info.set_size(width, height);
c_info.set_raw_data_in(true);
c_info.set_quality(parameters.jpeg.quality as f32);
c_info.set_progressive_mode();
c_info.set_optimize_coding(true);
c_info.set_optimize_scans(true);
c_info.set_mem_dest();
for (c, samp) in c_info.components_mut().iter_mut().zip(samp_factors) {
c.v_samp_factor = samp;
c.h_samp_factor = samp;
}
c_info.start_compress();
if parameters.keep_metadata {
markers.for_each(|marker| {
c_info.write_marker(marker.marker, marker.data);
});
}
let mut bitmaps = [&mut Vec::new(), &mut Vec::new(), &mut Vec::new()];
let mut d_info = decompress.raw()?;
d_info.read_raw_data(&mut bitmaps);
d_info.finish_decompress();
c_info.write_raw_data(&bitmaps.iter().map(|c| &c[..]).collect::<Vec<_>>());
c_info.finish_compress();
//data_to_vec does not return an error
let data = c_info.data_to_vec().unwrap();
let mut file = File::create(output_path)?;
file.write_all(&data)?;
Ok(())
}

119
src/lib.rs Normal file
View File

@ -0,0 +1,119 @@
mod utils;
mod jpeg;
mod png;
mod gif;
mod webp;
use std::error::Error;
use crate::utils::get_filetype;
use std::ffi::CStr;
use std::os::raw::c_char;
#[repr(C)]
pub struct C_CSParameters {
pub keep_metadata: bool,
pub jpeg_quality: u32,
pub png_level: u32,
pub png_force_zopfli: bool,
pub gif_quality: u32,
pub webp_quality: u32,
pub optimize: bool,
}
pub struct CSParameters {
pub jpeg: jpeg::Parameters,
pub png: png::Parameters,
pub gif: gif::Parameters,
pub webp: webp::Parameters,
pub keep_metadata: bool,
pub optimize: bool,
}
pub fn initialize_parameters() -> CSParameters
{
let jpeg = jpeg::Parameters {
quality: 80
};
let png = png::Parameters {
oxipng: oxipng::Options::default(),
level: 3,
force_zopfli: false,
};
let gif = gif::Parameters {
quality: 80
};
let webp = webp::Parameters {
quality: 80
};
CSParameters {
jpeg,
png,
gif,
webp,
keep_metadata: false,
optimize: false,
}
}
#[no_mangle]
pub extern fn c_compress(input_path: *const c_char, output_path: *const c_char, params: C_CSParameters) -> bool {
unsafe {
let mut parameters = initialize_parameters();
parameters.jpeg.quality = params.jpeg_quality;
parameters.png.level = params.png_level - 1;
parameters.optimize = params.optimize;
parameters.keep_metadata = params.keep_metadata;
parameters.png.force_zopfli = params.png_force_zopfli;
parameters.gif.quality = params.gif_quality;
parameters.webp.quality = params.webp_quality;
compress(CStr::from_ptr(input_path).to_str().unwrap().to_string(),
CStr::from_ptr(output_path).to_str().unwrap().to_string(),
parameters)
.unwrap();
true
}
}
pub fn compress(input_path: String, output_path: String, parameters: CSParameters) -> Result<(), Box<dyn Error>> {
let file_type = get_filetype(&input_path);
if parameters.jpeg.quality == 0 || parameters.jpeg.quality > 100 {
return Err("Invalid JPEG quality value".into());
}
if parameters.png.level > 6 {
return Err("Invalid PNG quality value".into());
}
if parameters.gif.quality > 100 {
return Err("Invalid GIF quality value".into());
}
if parameters.webp.quality > 100 {
return Err("Invalid WebP quality value".into());
}
match file_type {
utils::SupportedFileTypes::Jpeg => {
jpeg::compress(input_path, output_path, parameters)?;
}
utils::SupportedFileTypes::Png => {
png::compress(input_path, output_path, parameters)?;
}
utils::SupportedFileTypes::Gif => {
gif::compress(input_path, output_path, parameters)?;
}
utils::SupportedFileTypes::WebP => {
webp::compress(input_path, output_path, parameters)?;
}
_ => return Err("Unknown file type".into())
}
Ok(())
}

30
src/png.rs Normal file
View File

@ -0,0 +1,30 @@
use std::path::PathBuf;
use oxipng::{PngError};
use crate::CSParameters;
pub struct Parameters {
pub oxipng: oxipng::Options,
pub level: u32,
pub force_zopfli: bool
}
pub fn compress(input_path: String, output_path: String, parameters: CSParameters) -> Result<(), PngError> {
let in_file = oxipng::InFile::Path(PathBuf::from(input_path));
let out_file = oxipng::OutFile::Path(Some(PathBuf::from(output_path)));
let mut oxipng_options = parameters.png.oxipng;
if !parameters.keep_metadata {
oxipng_options.strip = oxipng::Headers::Safe;
}
if parameters.optimize && parameters.png.force_zopfli {
oxipng_options.deflate = oxipng::Deflaters::Zopfli;
} else {
let mut preset = parameters.png.level - 1;
if parameters.optimize {
preset = 6;
}
oxipng_options = oxipng::Options::from_preset(preset as u8);
}
oxipng::optimize(&in_file, &out_file, &oxipng_options)
}

20
src/utils.rs Normal file
View File

@ -0,0 +1,20 @@
pub enum SupportedFileTypes {
Jpeg,
Png,
Gif,
WebP,
Unkn,
}
pub fn get_filetype(file_path: &str) -> SupportedFileTypes {
match infer::get_from_path(file_path) {
Ok(v) => match v.unwrap().mime_type() {
"image/jpeg" => SupportedFileTypes::Jpeg,
"image/png" => SupportedFileTypes::Png,
"image/gif" => SupportedFileTypes::Gif,
"image/webp" => SupportedFileTypes::WebP,
_ => SupportedFileTypes::Unkn
},
Err(e) => panic!("{}", e)
}
}

37
src/webp.rs Normal file
View File

@ -0,0 +1,37 @@
use std::fs::File;
use std::io;
use std::io::{Read, Write};
use std::ops::Deref;
use webp;
use crate::CSParameters;
pub struct Parameters {
pub quality: u32,
}
pub fn compress(input_path: String, output_path: String, parameters: CSParameters) -> Result<(), io::Error>
{
let mut input_file = File::open(input_path)?;
let mut input_data = Vec::new();
input_file.read_to_end(&mut input_data)?;
let decoder = webp::Decoder::new(&input_data);
let input_webp = match decoder.decode() {
Some(img) => img,
None => return Err(io::Error::new(io::ErrorKind::Other, "WebP decode failed!"))
};
let input_image = input_webp.to_image();
let encoder = match webp::Encoder::from_image(&input_image) {
Ok(encoder) => encoder,
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e))
};
let mut output_file = File::create(output_path)?;
if parameters.optimize {
output_file.write_all(encoder.encode_lossless().deref())?;
} else {
output_file.write_all(encoder.encode(parameters.webp.quality as f32).deref())?;
}
Ok(())
}

88
tests/gif.rs Normal file
View File

@ -0,0 +1,88 @@
use std::sync::Once;
use std::fs;
static INIT: Once = Once::new();
pub fn initialize(file: &str) {
INIT.call_once(|| {
if fs::metadata(file).is_ok() {
fs::remove_file(file).unwrap();
}
});
}
pub fn cleanup(file: &str) {
if fs::metadata(file).is_ok() {
fs::remove_file(file).unwrap();
}
}
// #[test]
// fn compress_20() {
// let output = "tests/samples/output/compressed_20.gif";
// initialize(output);
// let mut params = caesium::initialize_parameters();
// params.gif.level = 20;
// caesium::compress(String::from("tests/samples/uncompressed_은하.gif"),
// String::from(output),
// params)
// .unwrap();
// assert!(std::path::Path::new(output).exists());
// cleanup(output)
// }
//
// #[test]
// fn compress_50() {
// let output = "tests/samples/output/compressed_50.gif";
// initialize(output);
// let mut params = caesium::initialize_parameters();
// params.gif.level = 50;
// caesium::compress(String::from("tests/samples/uncompressed_은하.gif"),
// String::from(output),
// params)
// .unwrap();
// assert!(std::path::Path::new(output).exists());
// cleanup(output)
// }
//
// #[test]
// fn compress_80() {
// let output = "tests/samples/output/compressed_80.gif";
// initialize(output);
// let mut params = caesium::initialize_parameters();
// params.gif.level = 80;
// caesium::compress(String::from("tests/samples/uncompressed_은하.gif"),
// String::from(output),
// params)
// .unwrap();
// assert!(std::path::Path::new(output).exists());
// cleanup(output)
// }
//
// #[test]
// fn compress_100() {
// let output = "tests/samples/output/compressed_100.gif";
// initialize(output);
// let mut params = caesium::initialize_parameters();
// params.gif.level = 100;
// caesium::compress(String::from("tests/samples/uncompressed_은하.gif"),
// String::from(output),
// params)
// .unwrap();
// assert!(std::path::Path::new(output).exists());
// cleanup(output)
// }
//
// #[test]
// fn optimize_gif() {
// let output = "tests/samples/output/optimized.gif";
// initialize(output);
// let mut params = caesium::initialize_parameters();
// params.optimize = true;
// caesium::compress(String::from("tests/samples/uncompressed_은하.gif"),
// String::from(output),
// params)
// .unwrap();
// assert!(std::path::Path::new(output).exists());
// cleanup(output)
// }

103
tests/jpeg.rs Normal file
View File

@ -0,0 +1,103 @@
use caesium;
use imgref::{Img, ImgVec};
use std::path::Path;
use dssim::{RGBAPLU, ToRGBAPLU, Val};
use load_image::ImageData;
use std::sync::Once;
use std::fs;
static INIT: Once = Once::new();
pub fn initialize(file: &str) {
INIT.call_once(|| {
if fs::metadata(file).is_ok() {
fs::remove_file(file).unwrap();
}
});
}
pub fn cleanup(file: &str) {
if fs::metadata(file).is_ok() {
fs::remove_file(file).unwrap();
}
}
fn load<P: AsRef<Path>>(path: P) -> Result<ImgVec<RGBAPLU>, lodepng::Error> {
let img = load_image::load_image(path.as_ref(), false)?;
match img.bitmap {
ImageData::RGB8(ref bitmap) => Ok(Img::new(bitmap.to_rgbaplu(), img.width, img.height)),
ImageData::RGB16(ref bitmap) => Ok(Img::new(bitmap.to_rgbaplu(), img.width, img.height)),
ImageData::RGBA8(ref bitmap) => Ok(Img::new(bitmap.to_rgbaplu(), img.width, img.height)),
ImageData::RGBA16(ref bitmap) => Ok(Img::new(bitmap.to_rgbaplu(), img.width, img.height)),
ImageData::GRAY8(ref bitmap) => Ok(Img::new(bitmap.to_rgbaplu(), img.width, img.height)),
ImageData::GRAY16(ref bitmap) => Ok(Img::new(bitmap.to_rgbaplu(), img.width, img.height)),
ImageData::GRAYA8(ref bitmap) => Ok(Img::new(bitmap.to_rgbaplu(), img.width, img.height)),
ImageData::GRAYA16(ref bitmap) => Ok(Img::new(bitmap.to_rgbaplu(), img.width, img.height)),
}
}
fn diff(compressed: &str) -> Val {
let attr = dssim::Dssim::new();
let orig = attr.create_image(&load("tests/samples/uncompressed_드림캐쳐.jpg").unwrap()).unwrap();
let comp = attr.create_image(&load(compressed).unwrap()).unwrap();
let (diff, _) = attr.compare(&orig, comp);
diff
}
#[test]
fn compress_100() {
let output = "tests/samples/output/compressed_100.jpg";
initialize(output);
let mut pars = caesium::initialize_parameters();
pars.jpeg.quality = 100;
caesium::compress(String::from("tests/samples/uncompressed_드림캐쳐.jpg"), String::from(output), pars).unwrap();
assert!(std::path::Path::new(output).exists());
cleanup(output)
}
#[test]
fn compress_80() {
let output = "tests/samples/output/compressed_80.jpg";
initialize(output);
let mut pars = caesium::initialize_parameters();
pars.jpeg.quality = 80;
caesium::compress(String::from("tests/samples/uncompressed_드림캐쳐.jpg"), String::from(output), pars).unwrap();
assert!(std::path::Path::new(output).exists());
cleanup(output)
}
#[test]
fn compress_50() {
let output = "tests/samples/output/compressed_50.jpg";
initialize(output);
let mut pars = caesium::initialize_parameters();
pars.jpeg.quality = 50;
caesium::compress(String::from("tests/samples/uncompressed_드림캐쳐.jpg"), String::from(output), pars).unwrap();
assert!(std::path::Path::new(output).exists());
cleanup(output)
}
#[test]
fn compress_10() {
let output = "tests/samples/output/compressed_10_드림캐쳐.jpg";
initialize(output);
let mut pars = caesium::initialize_parameters();
pars.jpeg.quality = 10;
caesium::compress(String::from("tests/samples/uncompressed_드림캐쳐.jpg"), String::from(output), pars).unwrap();
assert!(std::path::Path::new(output).exists());
cleanup(output)
}
#[test]
fn optimize_jpeg() {
let output = "tests/samples/output/compressed_optimized_드림캐쳐.jpg";
initialize(output);
let mut pars = caesium::initialize_parameters();
pars.optimize = true;
caesium::compress(String::from("tests/samples/uncompressed_드림캐쳐.jpg"), String::from(output), pars).unwrap();
assert!(std::path::Path::new(output).exists());
//Floats error
assert!(diff(output) < 0.001);
cleanup(output)
}

59
tests/metadata.rs Normal file
View File

@ -0,0 +1,59 @@
use std::sync::Once;
use std::fs;
use std::path::Path;
use std::fs::File;
use std::io::BufReader;
use exif::{Tag, In, Field};
static INIT: Once = Once::new();
pub fn initialize(file: &str) {
INIT.call_once(|| {
if fs::metadata(file).is_ok() {
fs::remove_file(file).unwrap();
}
});
}
pub fn cleanup(file: &str) {
if fs::metadata(file).is_ok() {
fs::remove_file(file).unwrap();
}
}
#[test]
fn compress_80_with_metadata() {
let output = "tests/samples/output/compressed_80_metadata.jpg";
initialize(output);
let mut pars = caesium::initialize_parameters();
pars.jpeg.quality = 80;
pars.keep_metadata = true;
caesium::compress(String::from("tests/samples/uncompressed_드림캐쳐.jpg"), String::from(output), pars).unwrap();
assert!(std::path::Path::new(output).exists());
let model = get_model_metadata(Path::new(output));
assert_eq!(model.display_value().to_string(), "\"Canon EOS 2000D\"");
cleanup(output)
}
#[test]
fn optimize_with_metadata() {
let output = "tests/samples/output/optimized_metadata.jpg";
initialize(output);
let mut pars = caesium::initialize_parameters();
pars.optimize = true;
pars.keep_metadata = true;
caesium::compress(String::from("tests/samples/uncompressed_드림캐쳐.jpg"), String::from(output), pars).unwrap();
assert!(std::path::Path::new(output).exists());
let model = get_model_metadata(Path::new(output));
assert_eq!(model.display_value().to_string(), "\"Canon EOS 2000D\"");
cleanup(output)
}
fn get_model_metadata(path: &Path) -> Field {
let file = File::open(path).unwrap();
let exif = exif::Reader::new().read_from_container(&mut BufReader::new(&file)).unwrap();
let f = exif.get_field(Tag::Model, In::PRIMARY).unwrap();
f.clone()
}

61
tests/png.rs Normal file
View File

@ -0,0 +1,61 @@
use caesium;
use std::sync::Once;
use std::fs;
static INIT: Once = Once::new();
pub fn initialize(file: &str) {
INIT.call_once(|| {
if fs::metadata(file).is_ok() {
fs::remove_file(file).unwrap();
}
});
}
pub fn cleanup(file: &str) {
if fs::metadata(file).is_ok() {
fs::remove_file(file).unwrap();
}
}
#[test]
fn standard_compress_png() {
let output = "tests/samples/output/compressed.png";
initialize(output);
caesium::compress(String::from("tests/samples/uncompressed_드림캐쳐.png"),
String::from(output),
caesium::initialize_parameters())
.unwrap();
assert!(std::path::Path::new(output).exists());
cleanup(output)
}
#[test]
fn standard_compress_png_with_optimize_flag() {
let output = "tests/samples/output/compressed_max.png";
initialize(output);
let mut params = caesium::initialize_parameters();
params.optimize = true;
caesium::compress(String::from("tests/samples/uncompressed_드림캐쳐.png"),
String::from(output),
params)
.unwrap();
assert!(std::path::Path::new(output).exists());
cleanup(output)
}
#[test]
fn zopfli_compress_png() {
let output = "tests/samples/output/optimized.png";
initialize(output);
let mut params = caesium::initialize_parameters();
params.png.level = 3;
params.optimize = true;
params.png.force_zopfli = true;
caesium::compress(String::from("tests/samples/uncompressed_드림캐쳐.png"),
String::from(output),
params)
.unwrap();
assert!(std::path::Path::new(output).exists());
cleanup(output)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

89
tests/webp.rs Normal file
View File

@ -0,0 +1,89 @@
use caesium;
use std::sync::Once;
use std::fs;
static INIT: Once = Once::new();
pub fn initialize(file: &str) {
INIT.call_once(|| {
if fs::metadata(file).is_ok() {
fs::remove_file(file).unwrap();
}
});
}
pub fn cleanup(file: &str) {
if fs::metadata(file).is_ok() {
fs::remove_file(file).unwrap();
}
}
#[test]
fn compress_20() {
let output = "tests/samples/output/compressed_20.webp";
initialize(output);
let mut params = caesium::initialize_parameters();
params.webp.quality = 20;
caesium::compress(String::from("tests/samples/uncompressed_家.webp"),
String::from(output),
params)
.unwrap();
assert!(std::path::Path::new(output).exists());
cleanup(output)
}
#[test]
fn compress_50() {
let output = "tests/samples/output/compressed_50.webp";
initialize(output);
let mut params = caesium::initialize_parameters();
params.webp.quality = 50;
caesium::compress(String::from("tests/samples/uncompressed_家.webp"),
String::from(output),
params)
.unwrap();
assert!(std::path::Path::new(output).exists());
cleanup(output)
}
#[test]
fn compress_80() {
let output = "tests/samples/output/compressed_80.webp";
initialize(output);
let mut params = caesium::initialize_parameters();
params.webp.quality = 80;
caesium::compress(String::from("tests/samples/uncompressed_家.webp"),
String::from(output),
params)
.unwrap();
assert!(std::path::Path::new(output).exists());
cleanup(output)
}
#[test]
fn compress_100() {
let output = "tests/samples/output/compressed_100.webp";
initialize(output);
let mut params = caesium::initialize_parameters();
params.webp.quality = 100;
caesium::compress(String::from("tests/samples/uncompressed_家.webp"),
String::from(output),
params)
.unwrap();
assert!(std::path::Path::new(output).exists());
cleanup(output)
}
#[test]
fn optimize() {
let output = "tests/samples/output/optimized.webp";
initialize(output);
let mut params = caesium::initialize_parameters();
params.optimize = true;
caesium::compress(String::from("tests/samples/uncompressed_家.webp"),
String::from(output),
params)
.unwrap();
assert!(std::path::Path::new(output).exists());
cleanup(output)
}