First commit.

fix-windows-compile
Wesley Castro 5 years ago
commit 2aa49c5797
  1. 52
      .gitignore
  2. 115
      BemaniLZ/BemaniLZ.c
  3. 11
      BemaniLZ/BemaniLZ.h
  4. 36
      CMakeLists.txt
  5. 9
      LICENSE
  6. 33
      README.md
  7. 21
      lodepng/LICENSE
  8. 6141
      lodepng/lodepng.c
  9. 1708
      lodepng/lodepng.h
  10. BIN
      pngquant/._MANUAL.md
  11. 36
      pngquant/COPYRIGHT
  12. 472
      pngquant/MANUAL.md
  13. 55
      pngquant/Makefile
  14. 112
      pngquant/blur.c
  15. 4
      pngquant/blur.h
  16. 206
      pngquant/configure
  17. 1642
      pngquant/libimagequant.c
  18. 109
      pngquant/libimagequant.h
  19. 507
      pngquant/mediancut.c
  20. 2
      pngquant/mediancut.h
  21. 63
      pngquant/mempool.c
  22. 13
      pngquant/mempool.h
  23. 241
      pngquant/nearest.c
  24. 8
      pngquant/nearest.h
  25. 278
      pngquant/pam.c
  26. 290
      pngquant/pam.h
  27. 89
      pngquant/viter.c
  28. 19
      pngquant/viter.h
  29. 382
      tcb-convert.c
  30. 153
      tcb-extract.c

52
.gitignore vendored

@ -0,0 +1,52 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf

@ -0,0 +1,115 @@
#include "BemaniLZ.h"
/* Based on BemaniLZ.cs from Scharfrichter */
static const unsigned int BUFFER_MASK = 0x3FF;
int decompress(uint8_t *src, int srcSize, uint8_t *dst, int dstSize)
{
int srcOffset = 0;
int dstOffset = 0;
uint8_t buffer[0x400];
int bufferOffset = 0;
uint8_t data = '\0';
uint32_t control = 0;
int32_t length = 0;
uint32_t distance = 0;
int loop = 0;
while(1)
{
loop = 0;
control >>= 1;
if (control < 0x100)
{
control = (uint8_t)src[srcOffset++] | 0xFF00;
//printf("control=%08X\n", control);
}
data = src[srcOffset++];
// direct copy
// can do stream of 1 - 8 direct copies
if ((control & 1) == 0)
{
//printf("%08X: direct copy %02X\n", dstOffset, data);
dst[dstOffset++] = data;
buffer[bufferOffset] = data;
bufferOffset = (bufferOffset + 1) & BUFFER_MASK;
continue;
}
// window copy (long distance)
if ((data & 0x80) == 0)
{
/*
input stream:
00: 0bbb bbaa
01: dddd dddd
distance: [0 - 1023] (00aa dddd dddd)
length: [2 - 33] (000b bbbb)
*/
distance = (uint8_t)src[srcOffset++] | ((data & 0x3) << 8);
length = (data >> 2) + 2;
loop = 1;
//printf("long distance: distance=%08X length=%08X data=%02X\n", distance, length, data);
//printf("%08X: window copy (long): %d bytes from %08X\n", dstOffset, length, dstOffset - distance);
}
// window copy (short distance)
else if ((data & 0x40) == 0)
{
/*
input stream:
00: llll dddd
distance: [1 - 16] (dddd)
length: [1 - 4] (llll)
*/
distance = (data & 0xF) + 1;
length = (data >> 4) - 7;
loop = 1;
//printf("short distance: distance=%08X length=%08X data=%02X\n", distance, length, data);
//printf("%08X: window copy (short): %d bytes from %08X\n", dstOffset, length, dstOffset - distance);
}
if (loop)
{
// copy length bytes from window
while(length-- >= 0)
{
data = buffer[(bufferOffset - distance) & BUFFER_MASK];
dst[dstOffset++] = data;
buffer[bufferOffset] = data;
bufferOffset = (bufferOffset + 1) & BUFFER_MASK;
}
continue;
}
// end of stream
if (data == 0xFF)
break;
// block copy
// directly copy group of bytes
/*
input stream:
00: llll lll0
length: [8 - 69]
directly copy (length+1) bytes
*/
length = data - 0xB9;
//printf("block copy %d bytes\n", length+1);
while (length-- >= 0)
{
data = src[srcOffset++];
dst[dstOffset++] = data;
buffer[bufferOffset] = data;
bufferOffset = (bufferOffset + 1) & BUFFER_MASK;
}
}
return dstOffset;
}

@ -0,0 +1,11 @@
#ifndef BEMANILZ_H
#define BEMANILZ_H
#include <stdint.h>
// Decompress data compressed with KonamiLZ
// Return value: final decompressed size
int decompress(uint8_t *src, int srcSize, uint8_t *dst, int dstSize);
#endif // BEMANILZ_H

@ -0,0 +1,36 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
project(tcbtools)
#
# Libraries
#
file(GLOB sources_pngquant
pngquant/*.c
)
add_library(pngquant ${sources_pngquant})
file(GLOB sources_lodepng
lodepng/*.c
)
add_library(lodepng ${sources_lodepng})
file(GLOB sources_bemanilz
BemaniLZ/*.c
)
add_library(bemanilz ${sources_bemanilz})
#
# Executables
#
file(GLOB sources_tcb_extract
tcb-extract.c
)
add_executable(tcb-extract ${sources_tcb_extract})
target_link_libraries(tcb-extract bemanilz)
file(GLOB sources_tcb_convert
tcb-convert.c
)
add_executable(tcb-convert ${sources_tcb_convert})
target_link_libraries(tcb-convert bemanilz pngquant lodepng)

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2015-2018 Wesley Castro
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,33 @@
# TCB Tools
Tools for extracting TCB image files from Dance Dance Revolution CS games.
## Building
```
mkdir build
cd build
cmake ..
make
```
## Usage
### tcb-extract
Search for and extract compressed and uncompressed TCB files within a file.
```
./tcb-extract <mode> <file containing TCBs>
```
Modes:
* 1 - Bruteforce search for compressed and uncompressed TCBs.
* 2 - Extract TCBs from file beginning with table (compressed entries).
* 3 - Extract TCBs from file beginning with table (uncompressed entries).
### tcb-convert
Convert TCB files to PNG images or inject PNG images back into TCB files. The extracted image will be a standard RGBA PNG image converted from either a 16 or 256 color palletized source. When injecting a PNG back into a TCB, the image data will be updated and a new pallete will be generated to match the TCB's original format. The PNG you inject must be the same resolution as the TCB.
```
./tcb-convert <mode> <TCB file>
```
Modes:
* e - Convert TCB to PNG.
* i - Inject PNG into TCB.

@ -0,0 +1,21 @@
Copyright (c) 2005-2018 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.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

@ -0,0 +1,36 @@
ยฉ 1997-2002 by Greg Roelofs; based on an idea by Stefan Schneider.
ยฉ 2009-2014 by Kornel Lesiล„ski.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ยฉ 1989, 1991 by Jef Poskanzer.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided
that the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation. This software is provided "as is" without express or
implied warranty.

@ -0,0 +1,472 @@
# libimagequantโ€”Image Quantization Library
Small, portable C library for high-quality conversion of RGBA images to 8-bit indexed-color (palette) images.
It's powering [pngquant2](http://pngquant.org).
## License
[BSD](https://raw.github.com/pornel/improved-pngquant/master/lib/COPYRIGHT).
It can be linked with both free and closed-source software.
## Download
The [library](http://pngquant.org/lib) is currently a part of the [pngquant2 project](https://github.com/pornel/improved-pngquant/tree/lib/lib).
Files needed for the library are only in the `lib/` directory inside the repository (and you can ignore the rest).
## Compiling and Linking
The library can be linked with ANSI C and C++ programs. It has no external dependencies.
To build on Unix-like systems run:
make -C lib
it will create `lib/libimagequant.a` which you can link with your program.
gcc yourprogram.c /path/to/lib/libimagequant.a
On BSD, use `gmake` (GNU make) rather than the native `make`.
Alternatively you can compile the library with your program simply by including all `.c` files (and define `NDEBUG` to get a fast version):
gcc -std=c99 -O3 -DNDEBUG lib/*.c yourprogram.c
### Compiling on Windows/Visual Studio
The library can be compiled with any C compiler that has at least basic support for C99 (GCC, clang, ICC, C++ Builder, even Tiny C Compiler), but Visual Studio 2012 and older are not up to date with the 1999 C standard. There are 2 options for using `libimagequant` on Windows:
* Use Visual Studio **2013** (MSVC 18) and an [MSVC-compatible branch of the library](https://github.com/pornel/pngquant/tree/msvc/lib)
* Or use GCC from [MinGW](http://www.mingw.org). Use GCC to build `libimagequant.a` (using the instructions above for Unix) and add it along with `libgcc.a` (shipped with the MinGW compiler) to your VC project.
## Overview
The basic flow is:
1. Create attributes object and configure the library.
2. Create image object from RGBA bitmap or data source.
3. Perform quantization (generate palette).
4. Store remapped image and final palette.
5. Free memory.
<p>
#include "lib/libimagequant.h"
liq_attr *attr = liq_attr_create();
liq_image *image = liq_image_create_rgba(attr, bitmap, width, height, 0);
liq_result *res = liq_quantize_image(attr, image);
liq_write_remapped_image(res, image, bitmap, bitmap_size);
const liq_palette *pal = liq_get_palette(res);
// use image and palette here
liq_attr_destroy(attr);
liq_image_destroy(image);
liq_result_destroy(res);
Functions returning `liq_error` return `LIQ_OK` (`0`) on success and non-zero on error.
It's safe to pass `NULL` to any function accepting `liq_attr`, `liq_image`, `liq_result` (in that case the error code `LIQ_INVALID_POINTER` will be returned). These objects can be reused multiple times.
There are 3 ways to create image object for quantization:
* `liq_image_create_rgba()` for simple, contiguous RGBA bitmaps (widthร—heightร—4 bytes large array).
* `liq_image_create_rgba_rows()` for non-contiguous RGBA bitmaps (that have padding between rows or reverse order, e.g. BMP).
* `liq_image_create_custom()` for RGB, ABGR, YUV and all other formats that can be converted on-the-fly to RGBA (you have to supply the conversion function).
## Functions
----
liq_attr* liq_attr_create(void);
Returns object that will hold initial settings (attributes) for the library. The object should be freed using `liq_attr_destroy()` after it's no longer needed.
Returns `NULL` in the unlikely case that the library cannot run on the current machine (e.g. the library has been compiled for SSE-capable x86 CPU and run on VIA C3 CPU).
----
liq_error liq_set_max_colors(liq_attr* attr, int colors);
Specifies maximum number of colors to use. The default is 256. Instead of setting a fixed limit it's better to use `liq_set_quality()`.
Returns `LIQ_VALUE_OUT_OF_RANGE` if number of colors is outside the range 2-256.
----
int liq_get_max_colors(liq_attr* attr);
Returns the value set by `liq_set_max_colors()`.
----
liq_error liq_set_quality(liq_attr* attr, int minimum, int maximum);
Quality is in range `0` (worst) to `100` (best) and values are analoguous to JPEG quality (i.e. `80` is usually good enough).
Quantization will attempt to use the lowest number of colors needed to achieve `maximum` quality. `maximum` value of `100` is the default and means conversion as good as possible.
If it's not possible to convert the image with at least `minimum` quality (i.e. 256 colors is not enough to meet the minimum quality), then `liq_quantize_image()` will fail. The default minumum is `0` (proceeds regardless of quality).
Quality measures how well the generated palette fits image given to `liq_quantize_image()`. If a different image is remapped with `liq_write_remapped_image()` then actual quality may be different.
Regardless of the quality settings the number of colors won't exceed the maximum (see `liq_set_max_colors()`).
Returns `LIQ_VALUE_OUT_OF_RANGE` if target is lower than minimum or any of them is outside the 0-100 range.
Returns `LIQ_INVALID_POINTER` if `attr` appears to be invalid.
liq_attr *attr = liq_attr_create();
liq_set_quality(attr, 50, 80); // use quality 80 if possible. Give up if quality drops below 50.
----
int liq_get_min_quality(liq_attr* attr);
Returns the lower bound set by `liq_set_quality()`.
----
int liq_get_max_quality(liq_attr* attr);
Returns the upper bound set by `liq_set_quality()`.
----
liq_image *liq_image_create_rgba(liq_attr *attr, void* bitmap, int width, int height, double gamma);
Creates image object that represents a bitmap later used for quantization and remapping. The bitmap must be contiguous run of RGBA pixels (alpha is the last component, 0 = transparent, 255 = opaque).
The bitmap must not be modified or freed until this object is freed with `liq_image_destroy()`. See also `liq_image_set_memory_ownership()`.
`width` and `height` are dimensions in pixels. An image 10x10 pixel large will need 400-byte bitmap.
`gamma` can be `0` for images with the typical 1/2.2 [gamma](http://en.wikipedia.org/wiki/Gamma_correction).
Otherwise `gamma` must be > 0 and < 1, e.g. `0.45455` (1/2.2) or `0.55555` (1/1.8). Generated palette will use the same gamma unless `liq_set_output_gamma()` is used. If `liq_set_output_gamma` is not used, then it only affects whether brighter or darker areas of the image will get more palette colors allocated.
Returns `NULL` on failure, e.g. if `bitmap` is `NULL` or `width`/`height` is <= 0.
----
liq_image *liq_image_create_rgba_rows(liq_attr *attr, void* rows[], int width, int height, double gamma);
Same as `liq_image_create_rgba()`, but takes array of pointers to rows in the bitmap. This allows defining bitmaps with reversed rows (like in BMP), "stride" different than width or using only fragment of a larger bitmap, etc.
`rows` array must have at least `height` elements and each row must be at least `width` RGBA pixels wide.
unsigned char *bitmap = โ€ฆ;
void *rows = malloc(height * sizeof(void*));
int bytes_per_row = width * 4 + padding; // stride
for(int i=0; i < height; i++) {
rows[i] = bitmap + i * bytes_per_row;
}
liq_image *img = liq_image_create_rgba_rows(attr, rows, width, height, 0);
// โ€ฆ
liq_image_destroy(img);
free(rows);
The row pointers and bitmap must not be modified or freed until this object is freed with `liq_image_destroy()` (you can change that with `liq_image_set_memory_ownership()`).
See also `liq_image_create_rgba()` and `liq_image_create_custom()`.
----
liq_result *liq_quantize_image(liq_attr *attr, liq_image *input_image);
Performs quantization (palette generation) based on settings in `attr` and pixels of the image.
Returns `NULL` if quantization fails, e.g. due to limit set in `liq_set_quality()`.
See `liq_write_remapped_image()`.
----
liq_error liq_set_dithering_level(liq_result *res, float dither_level);
Enables/disables dithering in `liq_write_remapped_image()`. Dithering level must be between `0` and `1` (inclusive). Dithering level `0` enables fast non-dithered remapping. Otherwise a variation of Floyd-Steinberg error diffusion is used.
Precision of the dithering algorithm depends on the speed setting, see `liq_set_speed()`.
Returns `LIQ_VALUE_OUT_OF_RANGE` if the dithering level is outside the 0-1 range.
----
liq_error liq_write_remapped_image(liq_result *result, liq_image *input_image, void *buffer, size_t buffer_size);
Remaps the image to palette and writes its pixels to the given buffer, 1 pixel per byte. Buffer must be large enough to fit the entire image, i.e. widthร—height bytes large. For safety, pass size of the buffer as `buffer_size`.
For best performance call `liq_get_palette()` *after* this function, as palette is improved during remapping.
Returns `LIQ_BUFFER_TOO_SMALL` if given size of the buffer is not enough to fit the entire image.
int buffer_size = width*height;
char *buffer = malloc(buffer_size);
if (LIQ_OK == liq_write_remapped_image(result, input_image, buffer, buffer_size)) {
liq_palette *pal = liq_get_palette(result);
// save image
}
See `liq_get_palette()` and `liq_write_remapped_image_rows()`.
----
const liq_palette *liq_get_palette(liq_result *result);
Returns pointer to palette optimized for image that has been quantized or remapped (final refinements are applied to the palette during remapping).
It's valid to call this method before remapping, if you don't plan to remap any images or want to use same palette for multiple images.
`liq_palette->count` contains number of colors (up to 256), `liq_palette->entries[n]` contains RGBA value for nth palette color.
The palette is **temporary and read-only**. You must copy the palette elsewhere *before* calling `liq_result_destroy()`.
Returns `NULL` on error.
----
void liq_attr_destroy(liq_attr *);
void liq_image_destroy(liq_image *);
void liq_result_destroy(liq_result *);
Releases memory owned by the given object. Object must not be used any more after it has been freed.
Freeing `liq_result` also frees any `liq_palette` obtained from it.
## Advanced Functions
----
liq_error liq_set_speed(liq_attr* attr, int speed);
Higher speed levels disable expensive algorithms and reduce quantization precision. The default speed is `3`. Speed `1` gives marginally better quality at significant CPU cost. Speed `10` has usually 5% lower quality, but is 8 times faster than the default.
High speeds combined with `liq_set_quality()` will use more colors than necessary and will be less likely to meet minimum required quality.
<table><caption>Features dependent on speed</caption>
<tr><th>Noise-sensitive dithering</th><td>speed 1 to 5</td></tr>
<tr><th>Forced posterization</th><td>8-10 or if image has more than million colors</td></tr>
<tr><th>Quantization error known</th><td>1-7 or if minimum quality is set</td></tr>
<tr><th>Additional quantization techniques</th><td>1-6</td></tr>
</table>
Returns `LIQ_VALUE_OUT_OF_RANGE` if the speed is outside the 1-10 range.
----
int liq_get_speed(liq_attr* attr);
Returns the value set by `liq_set_speed()`.
----
liq_error liq_set_min_opacity(liq_attr* attr, int min);
Alpha values higher than this will be rounded to opaque. This is a workaround for Internet Explorer 6 that truncates semitransparent values to completely transparent. The default is `255` (no change). 238 is a suggested value.
Returns `LIQ_VALUE_OUT_OF_RANGE` if the value is outside the 0-255 range.
----
int liq_get_min_opacity(liq_attr* attr);
Returns the value set by `liq_set_min_opacity()`.
----
liq_set_min_posterization(liq_attr* attr, int bits);
Ignores given number of least significant bits in all channels, posterizing image to `2^bits` levels. `0` gives full quality. Use `2` for VGA or 16-bit RGB565 displays, `4` if image is going to be output on a RGB444/RGBA4444 display (e.g. low-quality textures on Android).
Returns `LIQ_VALUE_OUT_OF_RANGE` if the value is outside the 0-4 range.
----
int liq_get_min_posterization(liq_attr* attr);
Returns the value set by `liq_set_min_posterization()`.
----
liq_set_last_index_transparent(liq_attr* attr, int is_last);
`0` (default) makes alpha colors sorted before opaque colors. Non-`0` mixes colors together except completely transparent color, which is moved to the end of the palette. This is a workaround for programs that blindly assume the last palette entry is transparent.
----
liq_image *liq_image_create_custom(liq_attr *attr, liq_image_get_rgba_row_callback *row_callback, void *user_info, int width, int height, double gamma);
<p>
void image_get_rgba_row_callback(liq_color row_out[], int row_index, int width, void *user_info) {
for(int column_index=0; column_index < width; column_index++) {
row_out[column_index] = /* generate pixel at (row_index, column_index) */;
}
}
Creates image object that will use callback to read image data. This allows on-the-fly conversion of images that are not in the RGBA color space.
`user_info` value will be passed to the callback. It may be useful for storing pointer to program's internal representation of the image.
The callback must read/generate `row_index`-th row and write its RGBA pixels to the `row_out` array. Row `width` is given for convenience and will always equal to image width.
The callback will be called multiple times for each row. Quantization and remapping require at least two full passes over image data, so caching of callback's work makes no sense โ€” in such case it's better to convert entire image and use `liq_image_create_rgba()` instead.
To use RGB image:
void rgb_to_rgba_callback(liq_color row_out[], int row_index, int width, void *user_info) {
unsigned char *rgb_row = ((unsigned char *)user_info) + 3*width*row_index;
for(int i=0; i < width; i++) {
row_out[i].r = rgb_row[i*3];
row_out[i].g = rgb_row[i*3+1];
row_out[i].b = rgb_row[i*3+2];
row_out[i].a = 255;
}
}
liq_image *img = liq_image_create_custom(attr, rgb_to_rgba_callback, rgb_bitmap, width, height, 0);
The library doesn't support RGB bitmaps "natively", because supporting only single format allows compiler to inline more code, 4-byte pixel alignment is faster, and SSE instructions operate on 4 values at once, so alpha support is almost free.
----
liq_error liq_image_set_memory_ownership(liq_image *image, int ownership_flags);
Passes ownership of bitmap and/or rows memory to the `liq_image` object, so you don't have to free it yourself. Memory owned by the object will be freed at its discretion with `free` function specified in `liq_attr_create_with_allocator()` (by default it's stdlib's `free()`).
* `LIQ_OWN_PIXELS` makes bitmap owned by the object. The bitmap will be freed automatically at any point when it's no longer needed. If you set this flag you must **not** free the bitmap yourself. If the image has been created with `liq_image_create_rgba_rows()` then the bitmap address is assumed to be the lowest address of any row.
* `LIQ_OWN_ROWS` makes array of row pointers (but not bitmap pointed by these rows) owned by the object. Rows will be freed when object is deallocated. If you set this flag you must **not** free the rows array yourself. This flag is valid only if the object has been created with `liq_image_create_rgba_rows()`.
These flags can be combined with binary *or*, i.e. `LIQ_OWN_PIXELS | LIQ_OWN_ROWS`.
This function must not be used if the image has been created with `liq_image_create_custom()`.
Returns `LIQ_VALUE_OUT_OF_RANGE` if invalid flags are specified or image is not backed by a bitmap.
----
liq_error liq_write_remapped_image_rows(liq_result *result, liq_image *input_image, unsigned char **row_pointers);
Similar to `liq_write_remapped_image()`. Writes remapped image, at 1 byte per pixel, to each row pointed by `row_pointers` array. The array must have at least as many elements as height of the image, and each row must have at least as many bytes as width of the image. Rows must not overlap.
For best performance call `liq_get_palette()` *after* this function, as remapping may change the palette.
Returns `LIQ_INVALID_POINTER` if `result` or `input_image` is `NULL`.
----
double liq_get_quantization_error(liq_result *result);
Returns mean square error of quantization (square of difference between pixel values in the original image and remapped image). Alpha channel and gamma correction are taken into account, so the result isn't exactly the mean square error of all channels.
For most images MSE 1-5 is excellent. 7-10 is OK. 20-30 will have noticeable errors. 100 is awful.
This function should be called *after* `liq_write_remapped_image()`. It may return `-1` if the value is not available (this is affected by `liq_set_speed()` and `liq_set_quality()`).
----
double liq_get_quantization_quality(liq_result *result);
Analoguous to `liq_get_quantization_error()`, but returns quantization error as quality value in the same 0-100 range that is used by `liq_set_quality()`.
This function should be called *after* `liq_write_remapped_image()`. It may return `-1` if the value is not available (this is affected by `liq_set_speed()` and `liq_set_quality()`).
This function can be used to add upper limit to quality options presented to the user, e.g.
liq_attr *attr = liq_attr_create();
liq_image *img = liq_image_create_rgba(โ€ฆ);
liq_result *res = liq_quantize_image(attr, img);
int max_attainable_quality = liq_get_quantization_quality(res);
printf("Please select quality between 0 and %d: ", max_attainable_quality);
int user_selected_quality = prompt();
if (user_selected_quality < max_attainable_quality) {
liq_set_quality(user_selected_quality, 0);
liq_result_destroy(res);
res = liq_quantize_image(attr, img);
}
liq_write_remapped_image(โ€ฆ);
----
void liq_set_log_callback(liq_attr*, liq_log_callback_function*, void *user_info);
<p>
void log_callback_function(const liq_attr*, const char *message, void *user_info) {}
----
void liq_set_log_flush_callback(liq_attr*, liq_log_flush_callback_function*, void *user_info);
<p>
void log_flush_callback_function(const liq_attr*, void *user_info) {}
Sets up callback function to be called when the library reports work progress or errors. The callback must not call any library functions.
`user_info` value will be passed to the callback.
`NULL` callback clears the current callback.
In the log callback the `message` is a zero-terminated string containing informative message to output. It is valid only until the callback returns.
`liq_set_log_flush_callback()` sets up callback function that will be called after the last log callback, which can be used to flush buffers and free resources used by the log callback.
----
liq_attr* liq_attr_create_with_allocator(void* (*malloc)(size_t), void (*free)(void*));
Same as `liq_attr_create`, but uses given `malloc` and `free` replacements to allocate all memory used by the library.
The `malloc` function must return 16-byte aligned memory on x86 (and on other architectures memory aligned for `double` and pointers). Conversely, if your stdlib's `malloc` doesn't return appropriately aligned memory, you should use this function to provide aligned replacements.
----
liq_attr* liq_attr_copy(liq_attr *orig);
Creates an independent copy of `liq_attr`. The copy should also be freed using `liq_attr_destroy()`.
---
liq_error liq_set_output_gamma(liq_result* res, double gamma);
Sets gamma correction for generated palette and remapped image. Must be > 0 and < 1, e.g. `0.45455` for gamma 1/2.2 in PNG images. By default output gamma is same as gamma of the input image.
----
int liq_image_get_width(const liq_image *img);
int liq_image_get_height(const liq_image *img);
double liq_get_output_gamma(const liq_result *result);
Getters for `width`, `height` and `gamma` of the input image.
If the input is invalid, these all return -1.
---
int liq_version();
Returns version of the library as an integer. Same as `LIQ_VERSION`. Human-readable version is defined as `LIQ_VERSION_STRING`.
## Multithreading
The library is stateless and doesn't use any global or thread-local storage. It doesn't use any locks.
* Different threads can perform unrelated quantizations/remappings at the same time (e.g. each thread working on a different image).
* The same `liq_attr`, `liq_result`, etc. can be accessed from different threads, but not at the same time (e.g. you can create `liq_attr` in one thread and free it in another).
The library needs to sort unique colors present in the image. Although the sorting algorithm does few things to make stack usage minimal in typical cases, there is no guarantee against extremely degenerate cases, so threads should have automatically growing stack.
### OpenMP
The library will parallelize some operations if compiled with OpenMP.
You must not increase number of maximum threads after `liq_image` has been created, as it allocates some per-thread buffers.
Callback of `liq_image_create_custom()` may be called from different threads at the same time.
## Acknowledgements
Thanks to Irfan Skiljan for helping test the first version of the library.
The library is developed by [Kornel Lesiล„ski](mailto:%20kornel@pngquant.org).

@ -0,0 +1,55 @@
-include config.mk
STATICLIB=libimagequant.a
DLL=libimagequant.dll
DLLIMP=libimagequant_dll.a
DLLDEF=libimagequant_dll.def
OBJS = pam.o mediancut.o blur.o mempool.o viter.o nearest.o libimagequant.o
BUILD_CONFIGURATION="$(CC) $(CFLAGS) $(LDFLAGS)"
DISTFILES = $(OBJS:.o=.c) *.h MANUAL.md COPYRIGHT Makefile configure
TARNAME = libimagequant-$(VERSION)
TARFILE = $(TARNAME)-src.tar.bz2
all: static
static: $(STATICLIB)
dll:
$(MAKE) CFLAGSADD="-DLIQ_EXPORT='__declspec(dllexport)'" $(DLL)
$(DLL) $(DLLIMP): $(OBJS)
$(CC) -fPIC -shared -o $(DLL) $(OBJS) $(LDFLAGS) -Wl,--out-implib,$(DLLIMP),--output-def,$(DLLDEF)
$(STATICLIB): $(OBJS)
$(AR) $(ARFLAGS) $@ $^
$(OBJS): $(wildcard *.h) config.mk
dist: $(TARFILE)
$(TARFILE): $(DISTFILES)
rm -rf $(TARFILE) $(TARNAME)
mkdir $(TARNAME)
cp $(DISTFILES) $(TARNAME)
tar -cjf $(TARFILE) --numeric-owner --exclude='._*' $(TARNAME)
rm -rf $(TARNAME)
-shasum $(TARFILE)
clean:
rm -f $(OBJS) $(STATICLIB) $(TARFILE) $(DLL) $(DLLIMP) $(DLLDEF)
distclean: clean
rm -f config.mk
config.mk:
ifeq ($(filter %clean %distclean, $(MAKECMDGOALS)), )
./configure
endif
.PHONY: all static clean dist distclean dll
.DELETE_ON_ERROR:

@ -0,0 +1,112 @@
#include "libimagequant.h"
#include "pam.h"
#include "blur.h"
/*
Blurs image horizontally (width 2*size+1) and writes it transposed to dst (called twice gives 2d blur)
*/
static void transposing_1d_blur(unsigned char *restrict src, unsigned char *restrict dst, unsigned int width, unsigned int height, const unsigned int size)
{
for(unsigned int j=0; j < height; j++) {
unsigned char *restrict row = src + j*width;
// accumulate sum for pixels outside line
unsigned int sum;
sum = row[0]*size;
for(unsigned int i=0; i < size; i++) {
sum += row[i];
}
// blur with left side outside line
for(unsigned int i=0; i < size; i++) {
sum -= row[0];
sum += row[i+size];
dst[i*height + j] = sum / (size*2);
}
for(unsigned int i=size; i < width-size; i++) {
sum -= row[i-size];
sum += row[i+size];
dst[i*height + j] = sum / (size*2);
}
// blur with right side outside line
for(unsigned int i=width-size; i < width; i++) {
sum -= row[i-size];
sum += row[width-1];
dst[i*height + j] = sum / (size*2);
}
}
}
/**
* Picks maximum of neighboring pixels (blur + lighten)
*/
LIQ_PRIVATE void liq_max3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height)
{
for(unsigned int j=0; j < height; j++) {
const unsigned char *row = src + j*width,
*prevrow = src + (j > 1 ? j-1 : 0)*width,
*nextrow = src + MIN(height-1,j+1)*width;
unsigned char prev,curr=row[0],next=row[0];
for(unsigned int i=0; i < width-1; i++) {
prev=curr;
curr=next;
next=row[i+1];
unsigned char t1 = MAX(prev,next);
unsigned char t2 = MAX(nextrow[i],prevrow[i]);
*dst++ = MAX(curr,MAX(t1,t2));
}
unsigned char t1 = MAX(curr,next);
unsigned char t2 = MAX(nextrow[width-1],prevrow[width-1]);
*dst++ = MAX(t1,t2);
}
}
/**
* Picks minimum of neighboring pixels (blur + darken)
*/
LIQ_PRIVATE void liq_min3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height)
{
for(unsigned int j=0; j < height; j++) {
const unsigned char *row = src + j*width,
*prevrow = src + (j > 1 ? j-1 : 0)*width,
*nextrow = src + MIN(height-1,j+1)*width;
unsigned char prev,curr=row[0],next=row[0];
for(unsigned int i=0; i < width-1; i++) {
prev=curr;
curr=next;
next=row[i+1];
unsigned char t1 = MIN(prev,next);
unsigned char t2 = MIN(nextrow[i],prevrow[i]);
*dst++ = MIN(curr,MIN(t1,t2));
}
unsigned char t1 = MIN(curr,next);
unsigned char t2 = MIN(nextrow[width-1],prevrow[width-1]);
*dst++ = MIN(t1,t2);
}
}
/*
Filters src image and saves it to dst, overwriting tmp in the process.
Image must be width*height pixels high. Size controls radius of box blur.
*/
LIQ_PRIVATE void liq_blur(unsigned char *src, unsigned char *tmp, unsigned char *dst, unsigned int width, unsigned int height, unsigned int size)
{
assert(size > 0);
if (width < 2*size+1 || height < 2*size+1) {
return;
}
transposing_1d_blur(src, tmp, width, height, size);
transposing_1d_blur(tmp, dst, height, width, size);
}

@ -0,0 +1,4 @@
LIQ_PRIVATE void liq_blur(unsigned char *src, unsigned char *tmp, unsigned char *dst, unsigned int width, unsigned int height, unsigned int size);
LIQ_PRIVATE void liq_max3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height);
LIQ_PRIVATE void liq_min3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height);

206
pngquant/configure vendored

@ -0,0 +1,206 @@
#!/usr/bin/env bash
CONFIG="config.mk"
PREFIX="/usr/local"
VERSION=$(grep LIQ_VERSION_STRING libimagequant.h | grep -Eo "2\.[0-9.]+")
DEBUG=
SSE=auto
OPENMP=
EXTRA_CFLAGS=
EXTRA_LDFLAGS=
# make gcc default compiler unless CC is already set
CC=${CC:-gcc}
help() {
printf "%4s %s\n" "" "$1"
}
for i in "$@"; do
case $i in
--help)
echo
help "--prefix= installation directory [$PREFIX]"
help "--extra-cflags= append to CFLAGS"
help "--extra-ldflags= append to LDFLAGS"
echo
help "--enable-debug"
help "--enable-sse/--disable-sse enable/disable SSE instructions"
echo
help "--with-openmp compile with multicore support"
echo
exit 0
;;
# Can be set before or after configure. Latter overrides former.
CC=*)
CC=${i#*=}
;;
CFLAGS=*)
CFLAGS=${i#*=}
;;
LDFLAGS=*)
LDFLAGS=${i#*=}
;;
--enable-debug)
DEBUG=1
;;
--enable-sse)
SSE=1
;;
--disable-sse)
SSE=0
;;
--with-openmp)
OPENMP=1
;;
--prefix=*)
PREFIX=${i#*=}
;;
# can be used multiple times or in quotes to set multiple flags
--extra-cflags=*)
EXTRA_CFLAGS="$EXTRA_CFLAGS ${i#*=}"
;;
--extra-ldflags=*)
EXTRA_LDFLAGS="$EXTRA_LDFLAGS ${i#*=}"
;;
*)
echo "error: unknown switch ${i%%=*}"
exit 1
;;
esac
done
# If someone runs sudo make install as very first command, and configure later,
# $CONFIG cannot be overwritten, and must be deleted before continuing.
if [[ -f "$CONFIG" && ! -w "$CONFIG" ]]; then
echo "Cannot overwrite file $CONFIG! Please delete it."
exit 1
fi
cflags() {
CFLAGS="$CFLAGS $1"
}
lflags() {
LDFLAGS="$LDFLAGS $1"
}
status() {
printf "%10s: %s\n" "$1" "$2"
}
# Append to CFLAGS if compiler supports flag, with optional prerequisite.
# Fails on errors and warnings.
conditional_cflags() {
if [ -z "$("$CC" -xc -S -o /dev/null $2 $1 <(echo) 2>&1)" ]; then
cflags "$1"
fi
}
error() {
status "$1" "error ... $2"
echo
exit 1
}
echo
# basic check
if ! "$CC" -xc -std=c99 <(echo "int main(){}") -o /dev/null &> /dev/null; then
error "Compiler" "$CC is no C compiler"
fi
status "Compiler" "$CC"
# init flags
CFLAGS=${CFLAGS:--O3 -fno-math-errno -funroll-loops -fomit-frame-pointer -Wall}
cflags "-std=c99 -I."
lflags "-lm lib/libimagequant.a"
# DEBUG
if [ -z "$DEBUG" ]; then
cflags "-DNDEBUG"
status "Debug" "no"
else
cflags "-g"
status "Debug" "yes"
fi
# SSE
if [ "$SSE" = 'auto' ]; then
if [[ "$(uname -m)" =~ (amd|x86_)64 ||
"$(grep -E -m1 "^flags" /proc/cpuinfo)" =~ "sse" ]]; then
SSE=1
fi
fi
if [ "$SSE" -eq 1 ]; then
status "SSE" "yes"
cflags "-DUSE_SSE=1"
cflags "-msse"
# Silence a later ICC warning due to -msse working slightly different.
conditional_cflags "-wd10121"
# Must be set explicitly for GCC on x86_32. Other compilers imply it.
conditional_cflags "-mfpmath=sse" "-msse"
elif [ "$SSE" -eq 0 ]; then
status "SSE" "no"
cflags "-DUSE_SSE=0"
fi
# OpenMP
if [ -n "$OPENMP" ]; then
if [[ "$("$CC" -xc -E -fopenmp <(echo -e \
"#ifdef _OPENMP
#include <omp.h>
#endif") 2>&1)" =~ "omp_get_thread_num" ]]; then
cflags "-fopenmp"
lflags "-fopenmp"
status "OpenMP" "yes"
else
error "OpenMP" "not supported by compiler"
fi
else
# silence warnings about omp pragmas
cflags "-Wno-unknown-pragmas"
conditional_cflags "-wd3180" # ICC
status "OpenMP" "no"
fi
echo
# As of GCC 4.5, 387 fp math is significantly slower in C99 mode without this.
# Note: CPUs without SSE2 use 387 for doubles, even when SSE fp math is set.
conditional_cflags "-fexcess-precision=fast"
# Intel C++ Compiler
# ICC does usually only produce fast(er) code when it can optimize to the full
# capabilites of the (Intel) CPU. This is equivalent to -march=native for GCC.
conditional_cflags "-xHOST"
# Disable unsafe fp optimizations and enforce fp precision as set in the source.
conditional_cflags "-fp-model source"
# Silence a gold linker warning about string misalignment.
conditional_cflags "-falign-stack=maintain-16-byte"
lflags "-lm" # Ubuntu requires this library last, issue #38
if [ -n "$EXTRA_CFLAGS" ]; then
cflags "$EXTRA_CFLAGS"
fi
if [ -n "$EXTRA_LDFLAGS" ]; then
lflags "$EXTRA_LDFLAGS"
fi
# Overwrite previous configuration.
echo "
# auto-generated by configure
PREFIX = $PREFIX
VERSION = $VERSION
CC = $CC
CFLAGS = $CFLAGS
LDFLAGS = $LDFLAGS
" > $CONFIG

File diff suppressed because it is too large Load Diff

@ -0,0 +1,109 @@
/*
* http://pngquant.org
*/
#ifndef LIBIMAGEQUANT_H
#define LIBIMAGEQUANT_H
#ifndef LIQ_EXPORT
#define LIQ_EXPORT extern
#endif
#define LIQ_VERSION 20301
#define LIQ_VERSION_STRING "2.3.1"
#ifndef LIQ_PRIVATE
#if defined(__GNUC__) || defined (__llvm__)
#define LIQ_PRIVATE __attribute__((visibility("hidden")))
#else
#define LIQ_PRIVATE
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
typedef struct liq_attr liq_attr;
typedef struct liq_image liq_image;
typedef struct liq_result liq_result;
typedef struct liq_color {
unsigned char r, g, b, a;
} liq_color;
typedef struct liq_palette {
unsigned int count;
liq_color entries[256];
} liq_palette;
typedef enum liq_error {
LIQ_OK = 0,
LIQ_QUALITY_TOO_LOW = 99,
LIQ_VALUE_OUT_OF_RANGE = 100,
LIQ_OUT_OF_MEMORY,
LIQ_NOT_READY,
LIQ_BITMAP_NOT_AVAILABLE,
LIQ_BUFFER_TOO_SMALL,
LIQ_INVALID_POINTER,
} liq_error;
enum liq_ownership {LIQ_OWN_ROWS=4, LIQ_OWN_PIXELS=8};
LIQ_EXPORT liq_attr* liq_attr_create(void);
LIQ_EXPORT liq_attr* liq_attr_create_with_allocator(void* (*malloc)(size_t), void (*free)(void*));
LIQ_EXPORT liq_attr* liq_attr_copy(liq_attr* orig);
LIQ_EXPORT void liq_attr_destroy(liq_attr* attr);
LIQ_EXPORT liq_error liq_set_max_colors(liq_attr* attr, int colors);
LIQ_EXPORT int liq_get_max_colors(const liq_attr* attr);
LIQ_EXPORT liq_error liq_set_speed(liq_attr* attr, int speed);
LIQ_EXPORT int liq_get_speed(const liq_attr* attr);
LIQ_EXPORT liq_error liq_set_min_opacity(liq_attr* attr, int min);
LIQ_EXPORT int liq_get_min_opacity(const liq_attr* attr);
LIQ_EXPORT liq_error liq_set_min_posterization(liq_attr* attr, int bits);
LIQ_EXPORT int liq_get_min_posterization(const liq_attr* attr);
LIQ_EXPORT liq_error liq_set_quality(liq_attr* attr, int minimum, int maximum);
LIQ_EXPORT int liq_get_min_quality(const liq_attr* attr);
LIQ_EXPORT int liq_get_max_quality(const liq_attr* attr);
LIQ_EXPORT void liq_set_last_index_transparent(liq_attr* attr, int is_last);
typedef void liq_log_callback_function(const liq_attr*, const char* message, void* user_info);
typedef void liq_log_flush_callback_function(const liq_attr*, void* user_info);
LIQ_EXPORT void liq_set_log_callback(liq_attr*, liq_log_callback_function*, void* user_info);
LIQ_EXPORT void liq_set_log_flush_callback(liq_attr*, liq_log_flush_callback_function*, void* user_info);
LIQ_EXPORT liq_image* liq_image_create_rgba_rows(liq_attr* attr, void* rows[], int width, int height, double gamma);
LIQ_EXPORT liq_image* liq_image_create_rgba(liq_attr* attr, void* bitmap, int width, int height, double gamma);
typedef void liq_image_get_rgba_row_callback(liq_color row_out[], int row, int width, void* user_info);
LIQ_EXPORT liq_image* liq_image_create_custom(liq_attr* attr, liq_image_get_rgba_row_callback* row_callback, void* user_info, int width, int height, double gamma);
LIQ_EXPORT liq_error liq_image_set_memory_ownership(liq_image* image, int ownership_flags);
LIQ_EXPORT int liq_image_get_width(const liq_image* img);
LIQ_EXPORT int liq_image_get_height(const liq_image* img);
LIQ_EXPORT void liq_image_destroy(liq_image* img);
LIQ_EXPORT liq_result* liq_quantize_image(liq_attr* options, liq_image* input_image);
LIQ_EXPORT liq_error liq_set_dithering_level(liq_result* res, float dither_level);
LIQ_EXPORT liq_error liq_set_output_gamma(liq_result* res, double gamma);
LIQ_EXPORT double liq_get_output_gamma(const liq_result* result);
LIQ_EXPORT const liq_palette* liq_get_palette(liq_result* result);
LIQ_EXPORT liq_error liq_write_remapped_image(liq_result* result, liq_image* input_image, void* buffer, size_t buffer_size);
LIQ_EXPORT liq_error liq_write_remapped_image_rows(liq_result* result, liq_image* input_image, unsigned char** row_pointers);
LIQ_EXPORT double liq_get_quantization_error(liq_result* result);
LIQ_EXPORT int liq_get_quantization_quality(liq_result* result);
LIQ_EXPORT void liq_result_destroy(liq_result*);
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,507 @@
/*
** Copyright (C) 1989, 1991 by Jef Poskanzer.
** Copyright (C) 1997, 2000, 2002 by Greg Roelofs; based on an idea by
** Stefan Schneider.
** ยฉ 2009-2013 by Kornel Lesinski.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation. This software is provided "as is" without express or
** implied warranty.
*/
#include <stdlib.h>
#include <stddef.h>
#include "libimagequant.h"
#include "pam.h"
#include "mediancut.h"
#define index_of_channel(ch) (offsetof(f_pixel,ch)/sizeof(float))
static f_pixel averagepixels(unsigned int clrs, const hist_item achv[], float min_opaque_val, const f_pixel center);
struct box {
f_pixel color;
f_pixel variance;
double sum, total_error, max_error;
unsigned int ind;
unsigned int colors;
};
ALWAYS_INLINE static double variance_diff(double val, const double good_enough);
inline static double variance_diff(double val, const double good_enough)
{
val *= val;
if (val < good_enough*good_enough) return val*0.25;
return val;
}
/** Weighted per-channel variance of the box. It's used to decide which channel to split by */
static f_pixel box_variance(const hist_item achv[], const struct box *box)
{
f_pixel mean = box->color;
double variancea=0, variancer=0, varianceg=0, varianceb=0;
for(unsigned int i = 0; i < box->colors; ++i) {
f_pixel px = achv[box->ind + i].acolor;
double weight = achv[box->ind + i].adjusted_weight;
variancea += variance_diff(mean.a - px.a, 2.0/256.0)*weight;
variancer += variance_diff(mean.r - px.r, 1.0/256.0)*weight;
varianceg += variance_diff(mean.g - px.g, 1.0/256.0)*weight;
varianceb += variance_diff(mean.b - px.b, 1.0/256.0)*weight;
}
return (f_pixel){
.a = variancea*(4.0/16.0),
.r = variancer*(7.0/16.0),
.g = varianceg*(9.0/16.0),
.b = varianceb*(5.0/16.0),
};
}
static double box_max_error(const hist_item achv[], const struct box *box)
{
f_pixel mean = box->color;
double max_error = 0;
for(unsigned int i = 0; i < box->colors; ++i) {
const double diff = colordifference(mean, achv[box->ind + i].acolor);
if (diff > max_error) {
max_error = diff;
}
}
return max_error;
}
ALWAYS_INLINE static double color_weight(f_pixel median, hist_item h);
static inline void hist_item_swap(hist_item *l, hist_item *r)
{
if (l != r) {
hist_item t = *l;
*l = *r;
*r = t;
}
}
ALWAYS_INLINE static unsigned int qsort_pivot(const hist_item *const base, const unsigned int len);
inline static unsigned int qsort_pivot(const hist_item *const base, const unsigned int len)
{
if (len < 32) {
return len/2;
}
const unsigned int aidx=8, bidx=len/2, cidx=len-1;
const unsigned int a=base[aidx].tmp.sort_value, b=base[bidx].tmp.sort_value, c=base[cidx].tmp.sort_value;
return (a < b) ? ((b < c) ? bidx : ((a < c) ? cidx : aidx ))
: ((b > c) ? bidx : ((a < c) ? aidx : cidx ));
}
ALWAYS_INLINE static unsigned int qsort_partition(hist_item *const base, const unsigned int len);
inline static unsigned int qsort_partition(hist_item *const base, const unsigned int len)
{
unsigned int l = 1, r = len;
if (len >= 8) {
hist_item_swap(&base[0], &base[qsort_pivot(base,len)]);
}
const unsigned int pivot_value = base[0].tmp.sort_value;
while (l < r) {
if (base[l].tmp.sort_value >= pivot_value) {
l++;
} else {
while(l < --r && base[r].tmp.sort_value <= pivot_value) {}
hist_item_swap(&base[l], &base[r]);
}
}
l--;
hist_item_swap(&base[0], &base[l]);
return l;
}
/** quick select algorithm */
static void hist_item_sort_range(hist_item *base, unsigned int len, unsigned int sort_start)
{
for(;;) {
const unsigned int l = qsort_partition(base, len), r = l+1;
if (l > 0 && sort_start < l) {
len = l;
}
else if (r < len && sort_start > r) {
base += r; len -= r; sort_start -= r;
}
else break;
}
}
/** sorts array to make sum of weights lower than halfvar one side, returns edge between <halfvar and >halfvar parts of the set */
static hist_item *hist_item_sort_halfvar(hist_item *base, unsigned int len, double *const lowervar, const double halfvar)
{
do {
const unsigned int l = qsort_partition(base, len), r = l+1;
// check if sum of left side is smaller than half,
// if it is, then it doesn't need to be sorted
unsigned int t = 0; double tmpsum = *lowervar;
while (t <= l && tmpsum < halfvar) tmpsum += base[t++].color_weight;
if (tmpsum < halfvar) {
*lowervar = tmpsum;
} else {
if (l > 0) {
hist_item *res = hist_item_sort_halfvar(base, l, lowervar, halfvar);
if (res) return res;
} else {
// End of left recursion. This will be executed in order from the first element.
*lowervar += base[0].color_weight;
if (*lowervar > halfvar) return &base[0];
}
}
if (len > r) {
base += r; len -= r; // tail-recursive "call"
} else {
*lowervar += base[r].color_weight;
return (*lowervar > halfvar) ? &base[r] : NULL;
}
} while(1);
}
static f_pixel get_median(const struct box *b, hist_item achv[]);
typedef struct {
unsigned int chan; float variance;
} channelvariance;
static int comparevariance(const void *ch1, const void *ch2)
{
return ((const channelvariance*)ch1)->variance > ((const channelvariance*)ch2)->variance ? -1 :
(((const channelvariance*)ch1)->variance < ((const channelvariance*)ch2)->variance ? 1 : 0);
}
/** Finds which channels need to be sorted first and preproceses achv for fast sort */
static double prepare_sort(struct box *b, hist_item achv[])
{
/*
** Sort dimensions by their variance, and then sort colors first by dimension with highest variance
*/
channelvariance channels[4] = {
{index_of_channel(r), b->variance.r},
{index_of_channel(g), b->variance.g},
{index_of_channel(b), b->variance.b},
{index_of_channel(a), b->variance.a},
};
qsort(channels, 4, sizeof(channels[0]), comparevariance);