├── pngquant ├── org │ └── pngquant │ │ ├── PngQuantException.java │ │ ├── LiqObject.java │ │ ├── Image.java │ │ ├── Result.java │ │ ├── PngQuant.java │ │ └── PngQuant.c ├── mediancut.h ├── nearest.h ├── imagequant.pc.in ├── blur.h ├── .gitignore ├── mempool.h ├── .travis.yml ├── kmeans.h ├── Cargo.toml ├── CONTRIBUTING.md ├── rust │ ├── build.rs │ └── libimagequant.rs ├── CHANGELOG ├── mempool.c ├── pom.xml ├── CODE_OF_CONDUCT.md ├── kmeans.c ├── blur.c ├── Makefile ├── libimagequant.cs ├── nearest.c ├── libimagequant.h ├── configure ├── pam.h ├── pam.c ├── mediancut.c └── COPYRIGHT ├── BemaniLZ ├── BemaniLZ.h └── BemaniLZ.c ├── .gitignore ├── CMakeLists.txt ├── lodepng └── LICENSE ├── LICENSE ├── README.md ├── tcb-extract.c ├── filedata-tool.py └── tcb-convert.c /pngquant/org/pngquant/PngQuantException.java: -------------------------------------------------------------------------------- 1 | package org.pngquant; 2 | 3 | public class PngQuantException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /pngquant/mediancut.h: -------------------------------------------------------------------------------- 1 | 2 | LIQ_PRIVATE colormap *mediancut(histogram *hist, unsigned int newcolors, const double target_mse, const double max_mse, void* (*malloc)(size_t), void (*free)(void*)); 3 | -------------------------------------------------------------------------------- /BemaniLZ/BemaniLZ.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef BEMANILZ_H 3 | #define BEMANILZ_H 4 | 5 | #include 6 | 7 | // Decompress data compressed with KonamiLZ 8 | // Return value: final decompressed size 9 | int decompress(uint8_t *src, int srcSize, uint8_t *dst, int dstSize); 10 | 11 | #endif // BEMANILZ_H 12 | -------------------------------------------------------------------------------- /pngquant/nearest.h: -------------------------------------------------------------------------------- 1 | // 2 | // nearest.h 3 | // pngquant 4 | // 5 | struct nearest_map; 6 | LIQ_PRIVATE struct nearest_map *nearest_init(const colormap *palette); 7 | LIQ_PRIVATE unsigned int nearest_search(const struct nearest_map *map, const f_pixel *px, const int palette_index_guess, float *diff); 8 | LIQ_PRIVATE void nearest_free(struct nearest_map *map); 9 | -------------------------------------------------------------------------------- /pngquant/imagequant.pc.in: -------------------------------------------------------------------------------- 1 | prefix=PREFIX 2 | includedir=${prefix}/include 3 | libdir=${prefix}/lib 4 | 5 | Name: imagequant 6 | Description: Small, portable C library for high-quality conversion of RGBA images to 8-bit indexed-color (palette) images. 7 | URL: https://pngquant.org/lib/ 8 | Version: VERSION 9 | Libs: -L${libdir} -limagequant 10 | Cflags: -I${includedir} 11 | -------------------------------------------------------------------------------- /pngquant/blur.h: -------------------------------------------------------------------------------- 1 | 2 | LIQ_PRIVATE void liq_blur(unsigned char *src, unsigned char *tmp, unsigned char *dst, unsigned int width, unsigned int height, unsigned int size); 3 | LIQ_PRIVATE void liq_max3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height); 4 | LIQ_PRIVATE void liq_min3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height); 5 | -------------------------------------------------------------------------------- /pngquant/.gitignore: -------------------------------------------------------------------------------- 1 | config.mk 2 | imagequant.pc 3 | *.lo 4 | *.o 5 | *.a 6 | *.so.0 7 | *.so 8 | *.bz2 9 | *.dylib 10 | *.dylib.0 11 | *.jnilib 12 | *.dSYM 13 | org/pngquant/*.class 14 | org/pngquant/*.h 15 | target/ 16 | msvc-dist/org/ 17 | msvc-dist/*.md 18 | msvc-dist/Makefile* 19 | msvc-dist/*.cs 20 | msvc-dist/*.xml 21 | msvc-dist/CHANGELOG 22 | msvc-dist/COPYRIGHT 23 | msvc-dist/configure 24 | msvc-dist/.gitignore 25 | quantized_example.png 26 | example 27 | lodepng.? 28 | -------------------------------------------------------------------------------- /pngquant/mempool.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMPOOL_H 2 | #define MEMPOOL_H 3 | 4 | #include 5 | 6 | struct mempool; 7 | typedef struct mempool *mempoolptr; 8 | 9 | LIQ_PRIVATE void* mempool_create(mempoolptr *mptr, const unsigned int size, unsigned int capacity, void* (*malloc)(size_t), void (*free)(void*)); 10 | LIQ_PRIVATE void* mempool_alloc(mempoolptr *mptr, const unsigned int size, const unsigned int capacity); 11 | LIQ_PRIVATE void mempool_destroy(mempoolptr m); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /pngquant/.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | include: 3 | - os: linux 4 | language: java 5 | jdk: oraclejdk9 6 | env: CFLAGS="-fPIC" 7 | install: true 8 | script: make java 9 | - os: linux 10 | language: rust 11 | script: cargo build --verbose --all && cargo test --verbose --all 12 | - os: linux 13 | language: generic 14 | script: make static 15 | - os: linux 16 | language: generic 17 | script: ./configure --prefix=/tmp && make libimagequant.so && make install 18 | -------------------------------------------------------------------------------- /pngquant/org/pngquant/LiqObject.java: -------------------------------------------------------------------------------- 1 | package org.pngquant; 2 | 3 | abstract class LiqObject { 4 | static { 5 | // libimagequant.jnilib or libimagequant.so must be in java.library.path 6 | System.loadLibrary("imagequant"); 7 | } 8 | 9 | long handle; 10 | 11 | /** 12 | * Free memory used by the library. The object must not be used after this call. 13 | */ 14 | abstract public void close(); 15 | 16 | protected void finalize() throws Throwable { 17 | close(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.6) 2 | 3 | project(tcbtools) 4 | 5 | # 6 | # Libraries 7 | # 8 | file(GLOB sources_pngquant 9 | pngquant/*.c 10 | ) 11 | add_library(pngquant ${sources_pngquant}) 12 | 13 | file(GLOB sources_lodepng 14 | lodepng/*.c 15 | ) 16 | add_library(lodepng ${sources_lodepng}) 17 | 18 | file(GLOB sources_bemanilz 19 | BemaniLZ/*.c 20 | ) 21 | add_library(bemanilz ${sources_bemanilz}) 22 | 23 | # 24 | # Executables 25 | # 26 | file(GLOB sources_tcb_extract 27 | tcb-extract.c 28 | ) 29 | add_executable(tcb-extract ${sources_tcb_extract}) 30 | target_link_libraries(tcb-extract bemanilz) 31 | 32 | file(GLOB sources_tcb_convert 33 | tcb-convert.c 34 | ) 35 | add_executable(tcb-convert ${sources_tcb_convert}) 36 | target_link_libraries(tcb-convert bemanilz pngquant lodepng) -------------------------------------------------------------------------------- /lodepng/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2018 Lode Vandevenne 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | -------------------------------------------------------------------------------- /pngquant/kmeans.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef KMEANS_H 3 | #define KMEANS_H 4 | 5 | // Spread memory touched by different threads at least 64B apart which I assume is the cache line size. This should avoid memory write contention. 6 | #define KMEANS_CACHE_LINE_GAP ((64+sizeof(kmeans_state)-1)/sizeof(kmeans_state)) 7 | 8 | typedef struct { 9 | double a, r, g, b, total; 10 | } kmeans_state; 11 | 12 | typedef void (*kmeans_callback)(hist_item *item, float diff); 13 | 14 | LIQ_PRIVATE void kmeans_init(const colormap *map, const unsigned int max_threads, kmeans_state state[]); 15 | LIQ_PRIVATE void kmeans_update_color(const f_pixel acolor, const float value, const colormap *map, unsigned int match, const unsigned int thread, kmeans_state average_color[]); 16 | LIQ_PRIVATE void kmeans_finalize(colormap *map, const unsigned int max_threads, const kmeans_state state[]); 17 | LIQ_PRIVATE double kmeans_do_iteration(histogram *hist, colormap *const map, kmeans_callback callback); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015-2018 Wesley Castro 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. -------------------------------------------------------------------------------- /pngquant/Cargo.toml: -------------------------------------------------------------------------------- 1 | # libimagequant is a pure C library. 2 | # Rust/Cargo is entirely optional. You can also use ./configure && make 3 | [package] 4 | version = "2.12.5" 5 | authors = ["Kornel Lesiński "] 6 | build = "rust/build.rs" 7 | categories = ["external-ffi-bindings"] 8 | homepage = "https://pngquant.org/lib" 9 | include = ["COPYRIGHT", "rust/*", "*.c", "*.h", "README.md", "Cargo.toml"] 10 | keywords = ["pngquant"] 11 | license = "GPL-3.0+" 12 | links = "imagequant" 13 | name = "imagequant-sys" 14 | readme = "README.md" 15 | repository = "https://github.com/ImageOptim/libimagequant" 16 | description = "Statically linked C part of imagequant library powering tools such as pngquant.\n\nThis library is dual-licensed like pngquant: either GPL or a commercial license. See website for details: https://pngquant.org" 17 | edition = "2018" 18 | 19 | [build-dependencies] 20 | cc = "1.0.38" 21 | 22 | [dependencies] 23 | rgb = "0.8.13" 24 | 25 | [dependencies.openmp-sys] 26 | optional = true 27 | version = "0.1.5" 28 | 29 | [features] 30 | default = ["sse"] 31 | openmp = ["openmp-sys"] 32 | openmp-static = ["openmp", "openmp-sys/static"] 33 | sse = [] 34 | 35 | [lib] 36 | crate-type = ["cdylib", "staticlib", "lib"] 37 | name = "imagequant_sys" 38 | path = "rust/libimagequant.rs" 39 | doctest = false 40 | -------------------------------------------------------------------------------- /pngquant/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | Thank you for contributing! pngquant and libimagequant are licensed under multiple 3 | licenses, so to make things clear, I'm accepting contributions as licensed under 4 | the BSD 2-clause license: 5 | 6 | Redistribution and use in source and binary forms, with or without modification, 7 | are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | -------------------------------------------------------------------------------- /pngquant/rust/build.rs: -------------------------------------------------------------------------------- 1 | //! This is a build script for Cargo https://crates.io/ 2 | //! It produces a static library that can be used by C or Rust. 3 | 4 | extern crate cc; 5 | 6 | use std::env; 7 | use std::path::PathBuf; 8 | use std::fs::canonicalize; 9 | 10 | fn main() { 11 | let mut cc = cc::Build::new(); 12 | let compiler = cc.get_compiler(); 13 | cc.warnings(false); 14 | 15 | if env::var("PROFILE").map(|p|p != "debug").unwrap_or(true) { 16 | cc.define("NDEBUG", Some("1")); 17 | } else { 18 | cc.define("DEBUG", Some("1")); 19 | } 20 | 21 | if cfg!(feature = "openmp") { 22 | cc.flag(&env::var("DEP_OPENMP_FLAG").unwrap()); 23 | } 24 | 25 | let target_arch = env::var("CARGO_CFG_TARGET_ARCH").expect("Needs CARGO_CFG_TARGET_ARCH"); 26 | if target_arch =="x86_64" || 27 | (target_arch == "x86" && cfg!(feature = "sse")) { 28 | cc.flag(if compiler.is_like_msvc() {"/arch:SSE2"} else {"-msse"}); 29 | cc.define("USE_SSE", Some("1")); 30 | } 31 | 32 | let outdated_c_compiler = env::var("TARGET").unwrap().contains("windows-msvc"); 33 | let has_msvc_files = PathBuf::from("msvc-dist/libimagequant.c").exists(); 34 | 35 | if outdated_c_compiler && has_msvc_files { 36 | println!("cargo:include={}", canonicalize("msvc-dist").unwrap().display()); 37 | cc.file("msvc-dist/libimagequant.c") 38 | .file("msvc-dist/nearest.c") 39 | .file("msvc-dist/kmeans.c") 40 | .file("msvc-dist/mediancut.c") 41 | .file("msvc-dist/mempool.c") 42 | .file("msvc-dist/pam.c") 43 | .file("msvc-dist/blur.c"); 44 | } else { 45 | // This is so that I don't forget to publish MSVC version as well 46 | if !has_msvc_files { 47 | println!("cargo:warning=msvc-dist/ directory not present. MSVC builds may fail"); 48 | } 49 | println!("cargo:include={}", canonicalize(".").unwrap().display()); 50 | if !compiler.is_like_msvc() { 51 | cc.flag("-std=c99"); 52 | } 53 | cc.file("libimagequant.c") 54 | .file("nearest.c") 55 | .file("kmeans.c") 56 | .file("mediancut.c") 57 | .file("mempool.c") 58 | .file("pam.c") 59 | .file("blur.c"); 60 | } 61 | 62 | cc.compile("libimagequant.a"); 63 | } 64 | -------------------------------------------------------------------------------- /pngquant/CHANGELOG: -------------------------------------------------------------------------------- 1 | version 2.12 2 | ------------ 3 | - new liq_histogram_add_fixed_color() 4 | - faster for large/complex images 5 | - workarounds for Microsoft's outdated C compiler 6 | 7 | version 2.11 8 | ------------ 9 | - new liq_image_set_background() for high-quality remapping of GIF frames 10 | - new liq_image_set_importance_map() for controlling which parts of the image get more palette colors 11 | - improved OpenMP support 12 | 13 | version 2.10 14 | ----------- 15 | - supports building with Rust/Cargo 16 | 17 | version 2.9 18 | ----------- 19 | - new liq_histogram_add_colors() 20 | 21 | version 2.8 22 | ----------- 23 | - standalone version 24 | - added Java interface (JNI) 25 | - new API for remapping multiple images to a single shared palette 26 | 27 | version 2.7 28 | ----------- 29 | - improved dithering of saturated and semitransparent colors 30 | - libimagequant reports detailed progress and supports aborting of operations via callbacks 31 | - fixed order of log output when using openmp 32 | 33 | version 2.5 34 | ----------- 35 | - replaced color search algorithm with vantage point tree, which is much faster and more reliable 36 | - deprecated IE6 workaround 37 | - warn when compiled without color profile support 38 | - improved support for predefined colors in libimagequant 39 | 40 | version 2.4 41 | ----------- 42 | - fixed remapping of bright colors when dithering 43 | - added libimagequant API to add fixed preset colors to the palette 44 | 45 | version 2.3 46 | ----------- 47 | - added ./configure script for better support of Intel C compiler and dependencies [thanks to pdknsk] 48 | - tweaked quality metric to better estimate quality of images with large solid backgrounds [thanks to Rolf Timmermans] 49 | - avoid applying quality setting to images that use palette already 50 | 51 | version 2.2 52 | ----------- 53 | - OpenMP acceleration 54 | - improved support for Intel C Compiler, speedup in 32-bit GCC, and some workarounds for Visual Studio's incomplete C support 55 | 56 | version 2.1 57 | ----------- 58 | - option to generate posterized output (for use with 16-bit textures) 59 | 60 | version 2.0 61 | ----------- 62 | - refactored codebase into pngquant and standalone libimagequant 63 | - reduced memory usage by further 30% (and more for very large images) 64 | - less precise remapping improving speed by 25% in higher speed settings 65 | - fixed regression in dithering of alpha channel 66 | -------------------------------------------------------------------------------- /pngquant/mempool.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** © 2009-2017 by Kornel Lesiński. 3 | ** © 1989, 1991 by Jef Poskanzer. 4 | ** © 1997, 2000, 2002 by Greg Roelofs; based on an idea by Stefan Schneider. 5 | ** 6 | ** See COPYRIGHT file for license. 7 | */ 8 | 9 | #include "libimagequant.h" 10 | #include "mempool.h" 11 | #include 12 | #include 13 | #include 14 | 15 | #define ALIGN_MASK 15UL 16 | #define MEMPOOL_RESERVED ((sizeof(struct mempool)+ALIGN_MASK) & ~ALIGN_MASK) 17 | 18 | struct mempool { 19 | unsigned int used, size; 20 | void* (*malloc)(size_t); 21 | void (*free)(void*); 22 | struct mempool *next; 23 | }; 24 | LIQ_PRIVATE void* mempool_create(mempoolptr *mptr, const unsigned int size, unsigned int max_size, void* (*malloc)(size_t), void (*free)(void*)) 25 | { 26 | if (*mptr && ((*mptr)->used+size) <= (*mptr)->size) { 27 | unsigned int prevused = (*mptr)->used; 28 | (*mptr)->used += (size+15UL) & ~0xFUL; 29 | return ((char*)(*mptr)) + prevused; 30 | } 31 | 32 | mempoolptr old = *mptr; 33 | if (!max_size) max_size = (1<<17); 34 | max_size = size+ALIGN_MASK > max_size ? size+ALIGN_MASK : max_size; 35 | 36 | *mptr = malloc(MEMPOOL_RESERVED + max_size); 37 | if (!*mptr) return NULL; 38 | **mptr = (struct mempool){ 39 | .malloc = malloc, 40 | .free = free, 41 | .size = MEMPOOL_RESERVED + max_size, 42 | .used = sizeof(struct mempool), 43 | .next = old, 44 | }; 45 | uintptr_t mptr_used_start = (uintptr_t)(*mptr) + (*mptr)->used; 46 | (*mptr)->used += (ALIGN_MASK + 1 - (mptr_used_start & ALIGN_MASK)) & ALIGN_MASK; // reserve bytes required to make subsequent allocations aligned 47 | assert(!(((uintptr_t)(*mptr) + (*mptr)->used) & ALIGN_MASK)); 48 | 49 | return mempool_alloc(mptr, size, size); 50 | } 51 | 52 | LIQ_PRIVATE void* mempool_alloc(mempoolptr *mptr, const unsigned int size, const unsigned int max_size) 53 | { 54 | if (((*mptr)->used+size) <= (*mptr)->size) { 55 | unsigned int prevused = (*mptr)->used; 56 | (*mptr)->used += (size + ALIGN_MASK) & ~ALIGN_MASK; 57 | return ((char*)(*mptr)) + prevused; 58 | } 59 | 60 | return mempool_create(mptr, size, max_size, (*mptr)->malloc, (*mptr)->free); 61 | } 62 | 63 | LIQ_PRIVATE void mempool_destroy(mempoolptr m) 64 | { 65 | while (m) { 66 | mempoolptr next = m->next; 67 | m->free(m); 68 | m = next; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DDR Tools 2 | Tools for extracting and modifying files from Dance Dance Revolution CS games. 3 | 4 | ## TCB Tools 5 | Tools for extracting and converting TCB image files. 6 | 7 | ### Building 8 | ``` 9 | mkdir build 10 | cd build 11 | cmake .. 12 | make 13 | ``` 14 | 15 | ### Usage 16 | 17 | #### tcb-extract 18 | Search for and extract compressed and uncompressed TCB files within a file. 19 | 20 | ``` 21 | ./tcb-extract 22 | ``` 23 | Modes: 24 | * `1` - Bruteforce search for compressed and uncompressed TCBs. 25 | * `2` - Extract TCBs from file beginning with table (compressed entries). 26 | * `3` - Extract TCBs from file beginning with table (uncompressed entries). 27 | 28 | In most cases `1` works best as many table variations exist that `2` and `3` 29 | won't work with. 30 | 31 | #### tcb-convert 32 | Convert TCB files to PNG images or inject PNG images back into TCB files. The 33 | extracted image will be a standard RGBA PNG image converted from either a 16 or 34 | 256 color palletized source. When injecting a PNG back into a TCB, the image 35 | data will be updated and a new pallete will be generated to match the TCB's 36 | original format. The PNG you inject must be the same resolution as the TCB. 37 | 38 | > Convert TCB to PNG: 39 | ``` 40 | ./tcb-convert e 41 | ``` 42 | 43 | > Inject PNG into TCB: 44 | ``` 45 | ./tcb-convert i 46 | ``` 47 | 48 | ## filedata-tool.py 49 | Extract and create filedata.bin files. 50 | 51 | ``` 52 | python3 filedata-tool.py 53 | ``` 54 | Modes: 55 | * `extract` - Extract the contents of `filedata.bin` to `directory`. All files 56 | referenced in the file table located in the game's `elf file` will be 57 | extracted in addition to "hidden" data missing from the file table. A CSV file 58 | will be created named `directory\fieldata.csv` containing IDs, offsets, and 59 | lengths found in the game's ELF, hidden file offsets and lengths, the exported 60 | filename, and a guessed description of the file contents to aid in 61 | modification. 62 | * `create` - Create `filedata.bin` using files in `directory`. The CSV created 63 | by the extraction mode is used to assemble a new file in the correct order and 64 | to update the file table in `elf file`. 65 | 66 | ### Tips 67 | * Don't modify the ids, offsets, or lengths in the CSV file created by the 68 | extraction mode. The filenames can be changed if desired. 69 | * Don't change the order of the rows in the CSV file. It matches the order of 70 | the file table found in the game's ELF. 71 | * New entries can't be added to the filetable, although this wouldn't be useful 72 | anyway. Instead, existing entries can be modified. 73 | -------------------------------------------------------------------------------- /pngquant/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | org.pngquant 5 | libimagequant 6 | jar 7 | 2.1.1 8 | pngquant 9 | http://pngquant.org 10 | 11 | . 12 | 13 | 14 | org.codehaus.mojo 15 | exec-maven-plugin 16 | 1.1 17 | 18 | 19 | build 20 | compile 21 | exec 22 | 23 | make 24 | 25 | -j8 26 | USE_SSE=1 27 | java 28 | 29 | 30 | 31 | 32 | clean 33 | clean 34 | exec 35 | 36 | make 37 | 38 | clean 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | mac-x64 49 | 50 | 51 | Mac 52 | x64 53 | 54 | 55 | 56 | 57 | org.pngquant 58 | libimagequant-jni 59 | 1 60 | jnilib 61 | mac-x64 62 | 63 | 64 | 65 | 66 | linux-x64 67 | 68 | 69 | unix 70 | Linux 71 | x64 72 | 73 | 74 | 75 | 76 | org.pngquant 77 | libimagequant-jni 78 | so 79 | 1 80 | linux-x64 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /pngquant/org/pngquant/Image.java: -------------------------------------------------------------------------------- 1 | package org.pngquant; 2 | 3 | import org.pngquant.*; 4 | import java.awt.image.*; 5 | 6 | /** 7 | * PngQuant's representation of an Image constructed from BufferedImage. 8 | */ 9 | public class Image extends LiqObject { 10 | 11 | /** 12 | * Converts BufferedImage to internal representation (pixel data is copied). 13 | * It's best to use BufferedImage in RGB/RGBA format backed by DataBufferByte. 14 | * Throws if conversion fails. 15 | */ 16 | public Image(BufferedImage image) throws PngQuantException { 17 | this(new PngQuant(), image); 18 | } 19 | 20 | public Image(PngQuant attr, BufferedImage image) throws PngQuantException { 21 | handle = handleFromImage(attr, image); 22 | 23 | if (handle == 0) { 24 | BufferedImage converted = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR); 25 | converted.getGraphics().drawImage(image, 0, 0, null); 26 | handle = handleFromImage(attr, converted); 27 | 28 | if (handle == 0) { 29 | throw new PngQuantException(); 30 | } 31 | } 32 | } 33 | 34 | /** 35 | * Guarantees presence of the given color in the palette (subject to setMaxColors()) 36 | * if this image is used for quantization. 37 | */ 38 | public native boolean addFixedColor(int r, int g, int b, int a); 39 | public boolean addFixedColor(int r, int g, int b) { 40 | return addFixedColor(r, g, b, 255); 41 | } 42 | public native int getWidth(); 43 | public native int getHeight(); 44 | 45 | public void close() { 46 | if (handle != 0) { 47 | liq_image_destroy(handle); 48 | handle = 0; 49 | } 50 | } 51 | 52 | private static long handleFromImage(PngQuant attr, BufferedImage image) { 53 | // The JNI wrapper will accept non-premultiplied ABGR and BGR only. 54 | int type = image.getType(); 55 | if (type != BufferedImage.TYPE_3BYTE_BGR && 56 | type != BufferedImage.TYPE_4BYTE_ABGR && 57 | type != BufferedImage.TYPE_4BYTE_ABGR_PRE) return 0; 58 | 59 | WritableRaster raster = image.getRaster(); 60 | ColorModel color = image.getColorModel(); 61 | if (type == BufferedImage.TYPE_4BYTE_ABGR_PRE) color.coerceData(raster, false); 62 | 63 | DataBuffer buffer = raster.getDataBuffer(); 64 | if (buffer instanceof DataBufferByte) { 65 | byte[] imageData = ((DataBufferByte)buffer).getData(); 66 | return liq_image_create(attr.handle, imageData, 67 | raster.getWidth(), raster.getHeight(), color.getNumComponents()); 68 | } 69 | return 0; 70 | } 71 | 72 | private static native long liq_image_create(long attr, byte[] bitmap, int width, int height, int components); 73 | private static native void liq_image_destroy(long handle); 74 | } 75 | -------------------------------------------------------------------------------- /BemaniLZ/BemaniLZ.c: -------------------------------------------------------------------------------- 1 | #include "BemaniLZ.h" 2 | 3 | /* Based on BemaniLZ.cs from Scharfrichter */ 4 | 5 | static const unsigned int BUFFER_MASK = 0x3FF; 6 | 7 | int decompress(uint8_t *src, int srcSize, uint8_t *dst, int dstSize) 8 | { 9 | int srcOffset = 0; 10 | int dstOffset = 0; 11 | 12 | uint8_t buffer[0x400]; 13 | int bufferOffset = 0; 14 | 15 | uint8_t data = '\0'; 16 | uint32_t control = 0; 17 | int32_t length = 0; 18 | uint32_t distance = 0; 19 | int loop = 0; 20 | 21 | while(1) 22 | { 23 | loop = 0; 24 | 25 | control >>= 1; 26 | if (control < 0x100) 27 | { 28 | control = (uint8_t)src[srcOffset++] | 0xFF00; 29 | //printf("control=%08X\n", control); 30 | } 31 | 32 | data = src[srcOffset++]; 33 | 34 | // direct copy 35 | // can do stream of 1 - 8 direct copies 36 | if ((control & 1) == 0) 37 | { 38 | //printf("%08X: direct copy %02X\n", dstOffset, data); 39 | dst[dstOffset++] = data; 40 | buffer[bufferOffset] = data; 41 | bufferOffset = (bufferOffset + 1) & BUFFER_MASK; 42 | continue; 43 | } 44 | 45 | // window copy (long distance) 46 | if ((data & 0x80) == 0) 47 | { 48 | /* 49 | input stream: 50 | 00: 0bbb bbaa 51 | 01: dddd dddd 52 | distance: [0 - 1023] (00aa dddd dddd) 53 | length: [2 - 33] (000b bbbb) 54 | */ 55 | distance = (uint8_t)src[srcOffset++] | ((data & 0x3) << 8); 56 | length = (data >> 2) + 2; 57 | loop = 1; 58 | //printf("long distance: distance=%08X length=%08X data=%02X\n", distance, length, data); 59 | //printf("%08X: window copy (long): %d bytes from %08X\n", dstOffset, length, dstOffset - distance); 60 | } 61 | 62 | // window copy (short distance) 63 | else if ((data & 0x40) == 0) 64 | { 65 | /* 66 | input stream: 67 | 00: llll dddd 68 | distance: [1 - 16] (dddd) 69 | length: [1 - 4] (llll) 70 | */ 71 | distance = (data & 0xF) + 1; 72 | length = (data >> 4) - 7; 73 | loop = 1; 74 | //printf("short distance: distance=%08X length=%08X data=%02X\n", distance, length, data); 75 | //printf("%08X: window copy (short): %d bytes from %08X\n", dstOffset, length, dstOffset - distance); 76 | } 77 | 78 | if (loop) 79 | { 80 | // copy length bytes from window 81 | while(length-- >= 0) 82 | { 83 | data = buffer[(bufferOffset - distance) & BUFFER_MASK]; 84 | dst[dstOffset++] = data; 85 | buffer[bufferOffset] = data; 86 | bufferOffset = (bufferOffset + 1) & BUFFER_MASK; 87 | } 88 | continue; 89 | } 90 | 91 | // end of stream 92 | if (data == 0xFF) 93 | break; 94 | 95 | // block copy 96 | // directly copy group of bytes 97 | /* 98 | input stream: 99 | 00: llll lll0 100 | length: [8 - 69] 101 | directly copy (length+1) bytes 102 | */ 103 | length = data - 0xB9; 104 | //printf("block copy %d bytes\n", length+1); 105 | while (length-- >= 0) 106 | { 107 | data = src[srcOffset++]; 108 | dst[dstOffset++] = data; 109 | buffer[bufferOffset] = data; 110 | bufferOffset = (bufferOffset + 1) & BUFFER_MASK; 111 | } 112 | } 113 | 114 | return dstOffset; 115 | } 116 | -------------------------------------------------------------------------------- /pngquant/org/pngquant/Result.java: -------------------------------------------------------------------------------- 1 | package org.pngquant; 2 | 3 | import org.pngquant.*; 4 | import java.awt.image.*; 5 | 6 | /** 7 | * Quantization result that holds palette and options for remapping. 8 | */ 9 | public class Result extends LiqObject { 10 | 11 | /** 12 | * Throws when quantization fails (e.g. due to failing to achieve minimum quality) 13 | */ 14 | public Result(PngQuant pngquant, Image image) throws PngQuantException { 15 | handle = liq_quantize_image(pngquant.handle, image.handle); 16 | if (handle == 0) { 17 | throw new PngQuantException(); 18 | } 19 | } 20 | 21 | /** 22 | * @return BufferedImage remapped to palette this Result has been created with or null on failure. 23 | */ 24 | public BufferedImage getRemapped(Image orig_image) { 25 | byte[] pal = liq_get_palette(handle); 26 | IndexColorModel color = new IndexColorModel(8, pal.length/4, pal, 0, true); 27 | BufferedImage img = new BufferedImage( 28 | orig_image.getWidth(), orig_image.getHeight(), 29 | BufferedImage.TYPE_BYTE_INDEXED, color); 30 | 31 | byte[] data = get8bitDataFromImage(img); 32 | if (data == null) return null; 33 | 34 | if (!liq_write_remapped_image(handle, orig_image.handle, data)) return null; 35 | 36 | return img; 37 | } 38 | 39 | /** 40 | * Dithering strength. Floyd-Steinberg is always used and in 41 | * speed settings 1-5 high-quality adaptive dithering is used. 42 | * @see PngQuant.setSpeed() 43 | * @link http://pngquant.org/lib/#liq_set_dithering_level 44 | * 45 | * @param dither_level Dithering in range 0 (none) and 1 (full) 46 | */ 47 | public native boolean setDitheringLevel(float dither_level); 48 | 49 | /** 50 | * The default is 0.45455 (1/2.2) which is PNG's approximation of sRGB. 51 | */ 52 | public native boolean setGamma(double gamma); 53 | public native double getGamma(); 54 | 55 | /** 56 | * Mean Square Error of remapping of image used to create this result. 57 | * @link http://pngquant.org/lib/#liq_get_quantization_error 58 | * 59 | * @return MSE or -1 if not available 60 | */ 61 | public native double getMeanSquareError(); 62 | 63 | /** 64 | * @link http://pngquant.org/lib/#liq_get_quantization_quality 65 | * @return Actually achieved quality in 0-100 range on scale compatible with PngQuant.setQuality() 66 | */ 67 | public native int getQuality(); 68 | 69 | public void close() { 70 | if (handle != 0) { 71 | liq_result_destroy(handle); 72 | handle = 0; 73 | } 74 | } 75 | 76 | private static byte[] get8bitDataFromImage(BufferedImage image) { 77 | if (image.getType() == BufferedImage.TYPE_BYTE_INDEXED) { 78 | DataBuffer buffer = image.getRaster().getDataBuffer(); 79 | if (buffer instanceof DataBufferByte) { 80 | return ((DataBufferByte)buffer).getData(); 81 | } 82 | } 83 | return null; 84 | } 85 | 86 | private static native byte[] liq_get_palette(long handle); 87 | private static native long liq_quantize_image(long attr, long image); 88 | private static native boolean liq_write_remapped_image(long handle, long image, byte[] buffer); 89 | private static native void liq_result_destroy(long handle); 90 | } 91 | -------------------------------------------------------------------------------- /pngquant/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at kornel@geekhood.net. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /pngquant/kmeans.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** © 2011-2016 by Kornel Lesiński. 3 | ** See COPYRIGHT file for license. 4 | */ 5 | 6 | #include "libimagequant.h" 7 | #include "pam.h" 8 | #include "kmeans.h" 9 | #include "nearest.h" 10 | #include 11 | #include 12 | 13 | #ifdef _OPENMP 14 | #include 15 | #else 16 | #define omp_get_max_threads() 1 17 | #define omp_get_thread_num() 0 18 | #endif 19 | 20 | /* 21 | * K-Means iteration: new palette color is computed from weighted average of colors that map to that palette entry. 22 | */ 23 | LIQ_PRIVATE void kmeans_init(const colormap *map, const unsigned int max_threads, kmeans_state average_color[]) 24 | { 25 | memset(average_color, 0, sizeof(average_color[0])*(KMEANS_CACHE_LINE_GAP+map->colors)*max_threads); 26 | } 27 | 28 | LIQ_PRIVATE void kmeans_update_color(const f_pixel acolor, const float value, const colormap *map, unsigned int match, const unsigned int thread, kmeans_state average_color[]) 29 | { 30 | match += thread * (KMEANS_CACHE_LINE_GAP+map->colors); 31 | average_color[match].a += acolor.a * value; 32 | average_color[match].r += acolor.r * value; 33 | average_color[match].g += acolor.g * value; 34 | average_color[match].b += acolor.b * value; 35 | average_color[match].total += value; 36 | } 37 | 38 | LIQ_PRIVATE void kmeans_finalize(colormap *map, const unsigned int max_threads, const kmeans_state average_color[]) 39 | { 40 | for (unsigned int i=0; i < map->colors; i++) { 41 | double a=0, r=0, g=0, b=0, total=0; 42 | 43 | // Aggregate results from all threads 44 | for(unsigned int t=0; t < max_threads; t++) { 45 | const unsigned int offset = (KMEANS_CACHE_LINE_GAP+map->colors) * t + i; 46 | 47 | a += average_color[offset].a; 48 | r += average_color[offset].r; 49 | g += average_color[offset].g; 50 | b += average_color[offset].b; 51 | total += average_color[offset].total; 52 | } 53 | 54 | if (total && !map->palette[i].fixed) { 55 | map->palette[i].acolor = (f_pixel){ 56 | .a = a / total, 57 | .r = r / total, 58 | .g = g / total, 59 | .b = b / total, 60 | }; 61 | map->palette[i].popularity = total; 62 | } 63 | } 64 | } 65 | 66 | LIQ_PRIVATE double kmeans_do_iteration(histogram *hist, colormap *const map, kmeans_callback callback) 67 | { 68 | const unsigned int max_threads = omp_get_max_threads(); 69 | LIQ_ARRAY(kmeans_state, average_color, (KMEANS_CACHE_LINE_GAP+map->colors) * max_threads); 70 | kmeans_init(map, max_threads, average_color); 71 | struct nearest_map *const n = nearest_init(map); 72 | hist_item *const achv = hist->achv; 73 | const int hist_size = hist->size; 74 | 75 | double total_diff=0; 76 | #if __GNUC__ >= 9 77 | #pragma omp parallel for if (hist_size > 2000) \ 78 | schedule(static) default(none) shared(achv,average_color,callback,hist_size,map,n) reduction(+:total_diff) 79 | #else 80 | #pragma omp parallel for if (hist_size > 2000) \ 81 | schedule(static) default(none) shared(average_color,callback) reduction(+:total_diff) 82 | #endif 83 | for(int j=0; j < hist_size; j++) { 84 | float diff; 85 | unsigned int match = nearest_search(n, &achv[j].acolor, achv[j].tmp.likely_colormap_index, &diff); 86 | achv[j].tmp.likely_colormap_index = match; 87 | total_diff += diff * achv[j].perceptual_weight; 88 | 89 | kmeans_update_color(achv[j].acolor, achv[j].perceptual_weight, map, match, omp_get_thread_num(), average_color); 90 | 91 | if (callback) callback(&achv[j], diff); 92 | } 93 | 94 | nearest_free(n); 95 | kmeans_finalize(map, max_threads, average_color); 96 | 97 | return total_diff / hist->total_perceptual_weight; 98 | } 99 | -------------------------------------------------------------------------------- /pngquant/org/pngquant/PngQuant.java: -------------------------------------------------------------------------------- 1 | package org.pngquant; 2 | 3 | import org.pngquant.*; 4 | import java.awt.image.*; 5 | 6 | /** 7 | * Starting point for the library. Holds configuration. Equivalent of liq_attr* in libimagequant. 8 | */ 9 | public class PngQuant extends LiqObject { 10 | 11 | /** 12 | * Single instance can be "recycled" for many remappings. 13 | */ 14 | public PngQuant() { 15 | handle = liq_attr_create(); 16 | } 17 | 18 | public PngQuant(PngQuant other) { 19 | handle = liq_attr_copy(other.handle); 20 | } 21 | 22 | /** 23 | * 1-shot quantization and remapping with current settings. 24 | * @see quantize() 25 | * 26 | * @return 8-bit indexed image or null on failure 27 | */ 28 | public BufferedImage getRemapped(BufferedImage bufimg) { 29 | try { 30 | Image liqimg = new Image(this, bufimg); 31 | BufferedImage remapped = getRemapped(liqimg); 32 | liqimg.close(); 33 | return remapped; 34 | } catch(PngQuantException e) { 35 | return null; 36 | } 37 | } 38 | 39 | /** @return remapped image or null on failure */ 40 | public BufferedImage getRemapped(Image liqimg) { 41 | Result result = quantize(liqimg); 42 | if (result == null) return null; 43 | BufferedImage remapped = result.getRemapped(liqimg); 44 | result.close(); 45 | return remapped; 46 | } 47 | 48 | /** 49 | * Performs quantization (chooses optimal palette for the given Image). 50 | * Returned object can be used to customize remapping and reused to remap other images to the same palette. 51 | * @link http://pngquant.org/lib/#liq_quantize_image 52 | * 53 | * @return null on failure 54 | */ 55 | public Result quantize(Image img) { 56 | try { 57 | return new Result(this, img); 58 | } catch(PngQuantException e) { 59 | return null; 60 | } 61 | } 62 | 63 | /** 64 | * Remapped images won't use more than given number of colors (may use less if setQuality() is used) 65 | * 66 | * @link http://pngquant.org/lib/#liq_set_max_colors 67 | */ 68 | public native boolean setMaxColors(int colors); 69 | 70 | /** 71 | * Equivalent of setQuality(target/2, target) 72 | * 73 | * @link http://pngquant.org/lib/#liq_set_quality 74 | */ 75 | public native boolean setQuality(int target); 76 | 77 | /** 78 | * Quality in range 0-100. Quantization will fail if minimum quality cannot 79 | * be achieved with given number of colors. 80 | * 81 | * @link http://pngquant.org/lib/#liq_set_quality 82 | */ 83 | public native boolean setQuality(int min, int max); 84 | 85 | /** 86 | * Speed in range 1 (slowest) and 11 (fastest). 3 is the optimum. 87 | * Higher speeds quantize quicker, but at cost of quality and sometimes larger images. 88 | * 89 | * @link http://pngquant.org/lib/#liq_set_speed 90 | */ 91 | public native boolean setSpeed(int speed); 92 | 93 | /** 94 | * Reduces color precision by truncating number of least significant bits. 95 | * Slightly improves speed and helps generating images for low-fidelity displays/textures. 96 | * 97 | * @link http://pngquant.org/lib/#liq_set_min_posterization 98 | */ 99 | public native boolean setMinPosterization(int bits); 100 | 101 | public void close() { 102 | if (handle != 0) { 103 | liq_attr_destroy(handle); 104 | handle = 0; 105 | } 106 | } 107 | 108 | private static native long liq_attr_create(); 109 | private static native long liq_attr_copy(long orig); 110 | private static native void liq_attr_destroy(long handle); 111 | } 112 | -------------------------------------------------------------------------------- /pngquant/blur.c: -------------------------------------------------------------------------------- 1 | /* 2 | © 2011-2015 by Kornel Lesiński. 3 | 4 | This file is part of libimagequant. 5 | 6 | libimagequant is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | libimagequant is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with libimagequant. If not, see . 18 | */ 19 | 20 | #include "libimagequant.h" 21 | #include "pam.h" 22 | #include "blur.h" 23 | 24 | /* 25 | Blurs image horizontally (width 2*size+1) and writes it transposed to dst (called twice gives 2d blur) 26 | */ 27 | static void transposing_1d_blur(unsigned char *restrict src, unsigned char *restrict dst, unsigned int width, unsigned int height, const unsigned int size) 28 | { 29 | assert(size > 0); 30 | 31 | for(unsigned int j=0; j < height; j++) { 32 | unsigned char *restrict row = src + j*width; 33 | 34 | // accumulate sum for pixels outside line 35 | unsigned int sum; 36 | sum = row[0]*size; 37 | for(unsigned int i=0; i < size; i++) { 38 | sum += row[i]; 39 | } 40 | 41 | // blur with left side outside line 42 | for(unsigned int i=0; i < size; i++) { 43 | sum -= row[0]; 44 | sum += row[i+size]; 45 | 46 | dst[i*height + j] = sum / (size*2); 47 | } 48 | 49 | for(unsigned int i=size; i < width-size; i++) { 50 | sum -= row[i-size]; 51 | sum += row[i+size]; 52 | 53 | dst[i*height + j] = sum / (size*2); 54 | } 55 | 56 | // blur with right side outside line 57 | for(unsigned int i=width-size; i < width; i++) { 58 | sum -= row[i-size]; 59 | sum += row[width-1]; 60 | 61 | dst[i*height + j] = sum / (size*2); 62 | } 63 | } 64 | } 65 | 66 | /** 67 | * Picks maximum of neighboring pixels (blur + lighten) 68 | */ 69 | LIQ_PRIVATE void liq_max3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height) 70 | { 71 | for(unsigned int j=0; j < height; j++) { 72 | const unsigned char *row = src + j*width, 73 | *prevrow = src + (j > 1 ? j-1 : 0)*width, 74 | *nextrow = src + MIN(height-1,j+1)*width; 75 | 76 | unsigned char prev,curr=row[0],next=row[0]; 77 | 78 | for(unsigned int i=0; i < width-1; i++) { 79 | prev=curr; 80 | curr=next; 81 | next=row[i+1]; 82 | 83 | unsigned char t1 = MAX(prev,next); 84 | unsigned char t2 = MAX(nextrow[i],prevrow[i]); 85 | *dst++ = MAX(curr,MAX(t1,t2)); 86 | } 87 | unsigned char t1 = MAX(curr,next); 88 | unsigned char t2 = MAX(nextrow[width-1],prevrow[width-1]); 89 | *dst++ = MAX(t1,t2); 90 | } 91 | } 92 | 93 | /** 94 | * Picks minimum of neighboring pixels (blur + darken) 95 | */ 96 | LIQ_PRIVATE void liq_min3(unsigned char *src, unsigned char *dst, unsigned int width, unsigned int height) 97 | { 98 | for(unsigned int j=0; j < height; j++) { 99 | const unsigned char *row = src + j*width, 100 | *prevrow = src + (j > 1 ? j-1 : 0)*width, 101 | *nextrow = src + MIN(height-1,j+1)*width; 102 | 103 | unsigned char prev,curr=row[0],next=row[0]; 104 | 105 | for(unsigned int i=0; i < width-1; i++) { 106 | prev=curr; 107 | curr=next; 108 | next=row[i+1]; 109 | 110 | unsigned char t1 = MIN(prev,next); 111 | unsigned char t2 = MIN(nextrow[i],prevrow[i]); 112 | *dst++ = MIN(curr,MIN(t1,t2)); 113 | } 114 | unsigned char t1 = MIN(curr,next); 115 | unsigned char t2 = MIN(nextrow[width-1],prevrow[width-1]); 116 | *dst++ = MIN(t1,t2); 117 | } 118 | } 119 | 120 | /* 121 | Filters src image and saves it to dst, overwriting tmp in the process. 122 | Image must be width*height pixels high. Size controls radius of box blur. 123 | */ 124 | LIQ_PRIVATE void liq_blur(unsigned char *src, unsigned char *tmp, unsigned char *dst, unsigned int width, unsigned int height, unsigned int size) 125 | { 126 | assert(size > 0); 127 | if (width < 2*size+1 || height < 2*size+1) { 128 | return; 129 | } 130 | transposing_1d_blur(src, tmp, width, height, size); 131 | transposing_1d_blur(tmp, dst, height, width, size); 132 | } 133 | -------------------------------------------------------------------------------- /pngquant/Makefile: -------------------------------------------------------------------------------- 1 | -include config.mk 2 | 3 | STATICLIB=libimagequant.a 4 | SHAREDLIB=libimagequant.$(SOLIBSUFFIX) 5 | SOVER=0 6 | ifeq ($(SOLIBSUFFIX),dylib) 7 | SHAREDLIBVER=libimagequant.$(SOVER).$(SOLIBSUFFIX) 8 | FIX_INSTALL_NAME=install_name_tool -id $(LIBDIR)/$(SHAREDLIBVER) $(DESTDIR)$(LIBDIR)/$(SHAREDLIBVER) 9 | else 10 | SHAREDLIBVER=libimagequant.$(SOLIBSUFFIX).$(SOVER) 11 | FIX_INSTALL_NAME= 12 | endif 13 | 14 | JNILIB=libimagequant.jnilib 15 | DLL=imagequant.dll 16 | DLLIMP=imagequant_dll.a 17 | DLLDEF=imagequant_dll.def 18 | JNIDLL=libimagequant.dll 19 | JNIDLLIMP=libimagequant_dll.a 20 | JNIDLLDEF=libimagequant_dll.def 21 | 22 | OBJS = pam.o mediancut.o blur.o mempool.o kmeans.o nearest.o libimagequant.o 23 | SHAREDOBJS = $(subst .o,.lo,$(OBJS)) 24 | 25 | JAVACLASSES = org/pngquant/LiqObject.class org/pngquant/PngQuant.class org/pngquant/Image.class org/pngquant/Result.class 26 | JAVAHEADERS = $(JAVACLASSES:.class=.h) 27 | JAVAINCLUDE = -I'$(JAVA_HOME)/include' -I'$(JAVA_HOME)/include/linux' -I'$(JAVA_HOME)/include/win32' -I'$(JAVA_HOME)/include/darwin' 28 | 29 | DISTFILES = $(OBJS:.o=.c) *.h README.md CHANGELOG COPYRIGHT Makefile configure imagequant.pc.in 30 | TARNAME = libimagequant-$(VERSION) 31 | TARFILE = $(TARNAME)-src.tar.bz2 32 | PKGCONFIG = imagequant.pc 33 | 34 | all: static shared 35 | 36 | static: $(STATICLIB) 37 | 38 | shared: $(SHAREDLIB) 39 | 40 | dll: 41 | $(MAKE) CFLAGS="$(CFLAGS) -DIMAGEQUANT_EXPORTS" $(DLL) 42 | 43 | java: $(JNILIB) 44 | 45 | java-dll: 46 | $(MAKE) CFLAGS="$(CFLAGS) -DIMAGEQUANT_EXPORTS" $(JNIDLL) 47 | 48 | $(DLL) $(DLLIMP): $(OBJS) 49 | $(CC) -fPIC -shared -o $(DLL) $^ $(LDFLAGS) -Wl,--out-implib,$(DLLIMP),--output-def,$(DLLDEF) 50 | 51 | $(STATICLIB): $(OBJS) 52 | $(AR) $(ARFLAGS) $@ $^ 53 | 54 | $(SHAREDOBJS): 55 | $(CC) -fPIC $(CFLAGS) -c $(@:.lo=.c) -o $@ 56 | 57 | libimagequant.so: $(SHAREDOBJS) 58 | $(CC) -shared -Wl,-soname,$(SHAREDLIBVER) -o $(SHAREDLIBVER) $^ $(LDFLAGS) 59 | ln -fs $(SHAREDLIBVER) $(SHAREDLIB) 60 | 61 | libimagequant.dylib: $(SHAREDOBJS) 62 | $(CC) -shared -o $(SHAREDLIBVER) $^ $(LDFLAGS) 63 | ln -fs $(SHAREDLIBVER) $(SHAREDLIB) 64 | 65 | $(OBJS): $(wildcard *.h) config.mk 66 | 67 | $(JNILIB): $(JAVAHEADERS) $(STATICLIB) org/pngquant/PngQuant.c 68 | $(CC) -g $(CFLAGS) $(LDFLAGS) $(JAVAINCLUDE) -shared -o $@ org/pngquant/PngQuant.c $(STATICLIB) 69 | 70 | $(JNIDLL) $(JNIDLLIMP): $(JAVAHEADERS) $(OBJS) org/pngquant/PngQuant.c 71 | $(CC) -fPIC -shared -I. $(JAVAINCLUDE) -o $(JNIDLL) $^ $(LDFLAGS) -Wl,--out-implib,$(JNIDLLIMP),--output-def,$(JNIDLLDEF) 72 | 73 | $(JAVACLASSES): %.class: %.java 74 | javac $< 75 | 76 | $(JAVAHEADERS): %.h: %.class 77 | javah -o $@ $(subst /,., $(patsubst %.class,%,$<)) && touch $@ 78 | 79 | dist: $(TARFILE) cargo 80 | 81 | $(TARFILE): $(DISTFILES) 82 | rm -rf $(TARFILE) $(TARNAME) 83 | mkdir $(TARNAME) 84 | cp $(DISTFILES) $(TARNAME) 85 | tar -cjf $(TARFILE) --numeric-owner --exclude='._*' $(TARNAME) 86 | rm -rf $(TARNAME) 87 | -shasum $(TARFILE) 88 | 89 | cargo: 90 | rm -rf msvc-dist 91 | git clone . -b msvc msvc-dist 92 | rm -rf msvc-dist/Cargo.toml msvc-dist/org msvc-dist/rust msvc-dist/README.md msvc-dist/COPYRIGHT 93 | cargo test 94 | 95 | example: example.c lodepng.h lodepng.c $(STATICLIB) 96 | $(CC) -g $(CFLAGS) -Wall example.c $(STATICLIB) -o example 97 | 98 | lodepng.h: 99 | curl -o lodepng.h -L https://raw.githubusercontent.com/lvandeve/lodepng/master/lodepng.h 100 | 101 | lodepng.c: 102 | curl -o lodepng.c -L https://raw.githubusercontent.com/lvandeve/lodepng/master/lodepng.cpp 103 | 104 | clean: 105 | rm -f $(OBJS) $(SHAREDOBJS) $(SHAREDLIBVER) $(SHAREDLIB) $(STATICLIB) $(TARFILE) $(DLL) '$(DLLIMP)' '$(DLLDEF)' 106 | rm -f $(JAVAHEADERS) $(JAVACLASSES) $(JNILIB) example 107 | 108 | distclean: clean 109 | rm -f config.mk 110 | rm -f imagequant.pc 111 | 112 | install: all $(PKGCONFIG) 113 | install -d $(DESTDIR)$(LIBDIR) 114 | install -d $(DESTDIR)$(PKGCONFIGDIR) 115 | install -d $(DESTDIR)$(INCLUDEDIR) 116 | install -m 644 $(STATICLIB) $(DESTDIR)$(LIBDIR)/$(STATICLIB) 117 | install -m 644 $(SHAREDLIBVER) $(DESTDIR)$(LIBDIR)/$(SHAREDLIBVER) 118 | ln -sf $(SHAREDLIBVER) $(DESTDIR)$(LIBDIR)/$(SHAREDLIB) 119 | install -m 644 $(PKGCONFIG) $(DESTDIR)$(PKGCONFIGDIR)/$(PKGCONFIG) 120 | install -m 644 libimagequant.h $(DESTDIR)$(INCLUDEDIR)/libimagequant.h 121 | $(FIX_INSTALL_NAME) 122 | 123 | uninstall: 124 | rm -f $(DESTDIR)$(LIBDIR)/$(STATICLIB) 125 | rm -f $(DESTDIR)$(LIBDIR)/$(SHAREDLIBVER) 126 | rm -f $(DESTDIR)$(LIBDIR)/$(SHAREDLIB) 127 | rm -f $(DESTDIR)$(PKGCONFIGDIR)/$(PKGCONFIG) 128 | rm -f $(DESTDIR)$(INCLUDEDIR)/libimagequant.h 129 | 130 | config.mk: 131 | ifeq ($(filter %clean %distclean, $(MAKECMDGOALS)), ) 132 | ./configure 133 | endif 134 | 135 | $(PKGCONFIG): config.mk 136 | sed 's|PREFIX|$(PREFIX)|;s|VERSION|$(VERSION)|' < imagequant.pc.in > $(PKGCONFIG) 137 | 138 | .PHONY: all static shared clean dist distclean dll java cargo 139 | .DELETE_ON_ERROR: 140 | -------------------------------------------------------------------------------- /tcb-extract.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "BemaniLZ/BemaniLZ.h" 6 | 7 | #if defined(_WIN32) 8 | #define MKDIR(a) _mkdir(a); 9 | #else 10 | #define MKDIR(a) mkdir(a, 0777); 11 | #endif 12 | 13 | int search_for_tcbs(const unsigned char *data, size_t dataLength, uint32_t *offsets, char *isCompressed) 14 | { 15 | int numFound = 0; 16 | 17 | for(unsigned int i = 0; i < dataLength; i++) 18 | { 19 | if(data[i] == 'T' && data[i+1] == 'C' && data[i+2] == 'B') 20 | { 21 | if(data[i+3] == 0 22 | && data[i+4] == 0 23 | && data[i+5] == 0 24 | && data[i+6] == 0 25 | && data[i+7] == 0 26 | && data[i+8] == 0 27 | && data[i+9] == 0 28 | && data[i+10] == 0) 29 | { 30 | offsets[numFound] = i; 31 | isCompressed[numFound] = 0; 32 | numFound++; 33 | } 34 | else if(i > 0 && (data[i-1] == 0x10 || data[i-1] == 0x90)) 35 | { 36 | offsets[numFound] = i - 1; 37 | isCompressed[numFound] = 1; 38 | numFound++; 39 | } 40 | } 41 | } 42 | 43 | return numFound; 44 | } 45 | 46 | int main(int argc, char *argv[]) 47 | { 48 | if(argc != 3) 49 | { 50 | printf("TCB Extractor\n"); 51 | printf("Usage:\n"); 52 | printf("%s \n", argv[0]); 53 | printf("Modes:\n"); 54 | printf("1 - Bruteforce search for compressed and uncompressed TCBs\n"); 55 | printf("2 - Extract from file beginning with table (compressed entries)\n"); 56 | printf("3 - Extract from file beginning with table (uncompressed entries)\n"); 57 | return EXIT_FAILURE; 58 | } 59 | 60 | FILE *compressed = fopen(argv[2], "rb"); 61 | if(!compressed) 62 | { 63 | printf("Error opening %s\n", argv[2]); 64 | return EXIT_FAILURE; 65 | } 66 | 67 | /* Get input data */ 68 | printf("Loading %s...\n", argv[2]); 69 | fseek(compressed, 0, SEEK_END); 70 | int length = ftell(compressed); 71 | fseek(compressed, 0, SEEK_SET); 72 | unsigned char *compressedData = malloc(length); 73 | fread(compressedData, length, 1, compressed); 74 | fclose(compressed); 75 | 76 | /* Search for TCBs */ 77 | printf("Searching for TCBs...\n"); 78 | uint32_t offsets[8000]; 79 | char isCompressed[8000] = {0}; 80 | int numEntries = 0; 81 | 82 | // file has table 83 | if(argv[1][0] == '2' || argv[1][0] == '3') 84 | { 85 | int offset = 0; 86 | uint32_t firstEntry = (uint32_t)compressedData[0]; 87 | 88 | if(firstEntry % 4) 89 | { 90 | printf("WARNING: First entry is number of entries to follow (%d).\n", firstEntry); 91 | } 92 | 93 | printf("first entry at 0x%08X\n", firstEntry); 94 | 95 | memcpy(offsets, compressedData, firstEntry); 96 | numEntries = firstEntry/4; 97 | 98 | if(argv[1][0] == '2') 99 | { 100 | for(int i = 0; i < numEntries; i++) 101 | { 102 | isCompressed[i] = 1; 103 | } 104 | } 105 | } 106 | else // no table, need to search for TCBs 107 | { 108 | numEntries = search_for_tcbs(compressedData, length, offsets, isCompressed); 109 | } 110 | 111 | offsets[numEntries] = length; 112 | 113 | printf("Found %d TCBs\n", numEntries); 114 | int numCompressed = 0; 115 | for(int i = 0; i < sizeof(isCompressed); i++) 116 | { 117 | if(isCompressed[i] == 1) 118 | numCompressed++; 119 | } 120 | printf("%d are compressed\n", numCompressed); 121 | 122 | /* Decompress and save TCBs */ 123 | char outFolderName[100]; 124 | snprintf(outFolderName, 100, "_%s", argv[2]); 125 | MKDIR(outFolderName); 126 | chdir(outFolderName); 127 | 128 | uint8_t *buffer = malloc(1024*1024*10); 129 | 130 | for(unsigned int i = 0; i < numEntries; i++) 131 | { 132 | if (offsets[i+1] == 0) 133 | break; 134 | 135 | printf("Extracting files: %04d/%d [%d%%]\r", i+1, numEntries, ((i+1)*100)/numEntries); 136 | 137 | int len = offsets[i+1] - offsets[i]; 138 | char filename[100]; 139 | snprintf(filename, 100, "%08X.tcb", offsets[i]); 140 | FILE *out = fopen(filename, "wb"); 141 | 142 | if(isCompressed[i]) 143 | { 144 | int decLen = decompress(compressedData + offsets[i], len, buffer, 1024*1024*10); 145 | fwrite(buffer, decLen, 1, out); 146 | } 147 | else 148 | { 149 | fwrite(compressedData + offsets[i], len, 1, out); 150 | } 151 | 152 | fclose(out); 153 | } 154 | 155 | printf("\n"); 156 | free(compressedData); 157 | free(buffer); 158 | 159 | return 0; 160 | } 161 | -------------------------------------------------------------------------------- /pngquant/org/pngquant/PngQuant.c: -------------------------------------------------------------------------------- 1 | #include "org/pngquant/PngQuant.h" 2 | #include "org/pngquant/Image.h" 3 | #include "org/pngquant/Result.h" 4 | #include "libimagequant.h" 5 | #include 6 | 7 | typedef struct { 8 | liq_image *image; 9 | jbyte *data; 10 | } liq_jni_image; 11 | 12 | static void *handle(JNIEnv *env, jobject obj) { 13 | jlong h = (*env)->GetLongField(env, obj, (*env)->GetFieldID(env, (*env)->GetObjectClass(env, obj), "handle", "J")); 14 | return (void*)h; 15 | } 16 | 17 | JNIEXPORT jlong JNICALL Java_org_pngquant_PngQuant_liq_1attr_1create(JNIEnv *env, jclass class) { 18 | return (jlong)liq_attr_create(); 19 | } 20 | 21 | JNIEXPORT jlong JNICALL Java_org_pngquant_PngQuant_liq_1attr_1copy(JNIEnv *env, jclass class, jlong attr) { 22 | return (jlong)liq_attr_copy((liq_attr*)attr); 23 | } 24 | 25 | JNIEXPORT void JNICALL Java_org_pngquant_PngQuant_liq_1attr_1destroy(JNIEnv *env, jclass class, jlong attr) { 26 | return liq_attr_destroy((liq_attr*)attr); 27 | } 28 | 29 | JNIEXPORT jboolean JNICALL Java_org_pngquant_PngQuant_setMaxColors(JNIEnv *env, jobject obj, jint colors) { 30 | return LIQ_OK == liq_set_max_colors(handle(env, obj), colors); 31 | } 32 | 33 | JNIEXPORT jboolean JNICALL Java_org_pngquant_PngQuant_setSpeed(JNIEnv *env, jobject obj, jint speed) { 34 | return LIQ_OK == liq_set_speed(handle(env, obj), speed); 35 | } 36 | 37 | JNIEXPORT jboolean JNICALL Java_org_pngquant_PngQuant_setMinPosterization(JNIEnv *env, jobject obj, jint p) { 38 | return LIQ_OK == liq_set_min_posterization(handle(env, obj), p); 39 | } 40 | 41 | JNIEXPORT jboolean JNICALL Java_org_pngquant_PngQuant_setQuality__I(JNIEnv *env, jobject obj, jint q) { 42 | return LIQ_OK == liq_set_quality(handle(env, obj), q/2, q); 43 | } 44 | 45 | JNIEXPORT jboolean JNICALL Java_org_pngquant_PngQuant_setQuality__II(JNIEnv *env, jobject obj, jint qmin, jint qmax) { 46 | return LIQ_OK == liq_set_quality(handle(env, obj), qmin, qmax); 47 | } 48 | 49 | static void convert_abgr(liq_color row_out[], int row_index, int width, void* user_info) { 50 | liq_jni_image *jniimg = user_info; 51 | int column_index; 52 | for(column_index=0; column_index < width; column_index++) { 53 | row_out[column_index].r = jniimg->data[4*(width*row_index + column_index) + 3]; 54 | row_out[column_index].g = jniimg->data[4*(width*row_index + column_index) + 2]; 55 | row_out[column_index].b = jniimg->data[4*(width*row_index + column_index) + 1]; 56 | row_out[column_index].a = jniimg->data[4*(width*row_index + column_index) + 0]; 57 | } 58 | } 59 | 60 | static void convert_bgr(liq_color row_out[], int row_index, int width, void* user_info) { 61 | liq_jni_image *jniimg = user_info; 62 | int column_index; 63 | for(column_index=0; column_index < width; column_index++) { 64 | row_out[column_index].r = jniimg->data[3*(width*row_index + column_index) + 2]; 65 | row_out[column_index].g = jniimg->data[3*(width*row_index + column_index) + 1]; 66 | row_out[column_index].b = jniimg->data[3*(width*row_index + column_index) + 0]; 67 | row_out[column_index].a = 255; 68 | } 69 | } 70 | 71 | JNIEXPORT jlong JNICALL Java_org_pngquant_Image_liq_1image_1create(JNIEnv *env, jclass class, jlong attr, jbyteArray bytearray, jint w, jint h, jint components) { 72 | /* liq_image needs to be wrapped to keep track of allocated buffer */ 73 | liq_jni_image *jniimg = malloc(sizeof(liq_jni_image)); 74 | 75 | /* copying buffer, since ReleaseByteArrayElements was crashing when called from finalize() */ 76 | jsize size = (*env)->GetArrayLength(env, bytearray); 77 | jniimg->data = malloc(size); 78 | (*env)->GetByteArrayRegion(env, bytearray, 0, size, jniimg->data); 79 | 80 | jniimg->image = liq_image_create_custom((liq_attr*)attr, components == 4 ? convert_abgr : convert_bgr, jniimg, w, h, 0); 81 | 82 | if (!jniimg->image) { 83 | free(jniimg->data); 84 | free(jniimg); 85 | return 0; 86 | } 87 | return (jlong)jniimg; 88 | } 89 | 90 | JNIEXPORT jboolean JNICALL Java_org_pngquant_Image_addFixedColor(JNIEnv *env, jobject obj, jint r, jint g, jint b, jint a) { 91 | liq_color c = {r,g,b,a}; 92 | return LIQ_OK == liq_image_add_fixed_color(((liq_jni_image*)handle(env,obj))->image, c); 93 | } 94 | 95 | JNIEXPORT jint JNICALL Java_org_pngquant_Image_getWidth(JNIEnv *env, jobject obj) { 96 | return liq_image_get_width(((liq_jni_image*)handle(env,obj))->image); 97 | } 98 | 99 | JNIEXPORT jint JNICALL Java_org_pngquant_Image_getHeight(JNIEnv *env, jobject obj) { 100 | return liq_image_get_height(((liq_jni_image*)handle(env,obj))->image); 101 | } 102 | 103 | JNIEXPORT void JNICALL Java_org_pngquant_Image_liq_1image_1destroy(JNIEnv *env, jclass class, jlong handle) { 104 | liq_jni_image *jniimg = (liq_jni_image*)handle; 105 | liq_image_destroy(jniimg->image); 106 | free(jniimg->data); 107 | free(jniimg); 108 | } 109 | 110 | JNIEXPORT jlong JNICALL Java_org_pngquant_Result_liq_1quantize_1image(JNIEnv *env, jclass class, jlong attr, jlong handle) { 111 | return (jlong)liq_quantize_image((liq_attr*)attr, ((liq_jni_image*)handle)->image); 112 | } 113 | 114 | JNIEXPORT jboolean JNICALL Java_org_pngquant_Result_setDitheringLevel(JNIEnv *env, jobject obj, jfloat l) { 115 | return LIQ_OK == liq_set_dithering_level(handle(env, obj), l); 116 | } 117 | 118 | JNIEXPORT jboolean JNICALL Java_org_pngquant_Result_setGamma(JNIEnv *env, jobject obj, jdouble gamma) { 119 | return LIQ_OK == liq_set_output_gamma(handle(env, obj), gamma); 120 | } 121 | 122 | JNIEXPORT jdouble JNICALL Java_org_pngquant_Result_getGamma(JNIEnv *env, jobject obj) { 123 | return liq_get_output_gamma(handle(env, obj)); 124 | } 125 | 126 | JNIEXPORT jboolean JNICALL Java_org_pngquant_Result_liq_1write_1remapped_1image(JNIEnv *env, jclass class, jlong result, jlong image_handle, jbyteArray bytearray) { 127 | jsize size = (*env)->GetArrayLength(env, bytearray); 128 | 129 | jbyte *bitmap = (*env)->GetByteArrayElements(env, bytearray, 0); 130 | liq_error err = liq_write_remapped_image((liq_result*)result, ((liq_jni_image*)image_handle)->image, bitmap, size); 131 | (*env)->ReleaseByteArrayElements(env, bytearray, bitmap, 0); 132 | 133 | return LIQ_OK == err; 134 | } 135 | 136 | JNIEXPORT jdouble JNICALL Java_org_pngquant_Result_getMeanSquareError(JNIEnv *env, jobject obj) { 137 | return liq_get_quantization_error(handle(env, obj)); 138 | } 139 | 140 | JNIEXPORT jint JNICALL Java_org_pngquant_Result_getQuality(JNIEnv *env, jobject obj) { 141 | return liq_get_quantization_quality(handle(env, obj)); 142 | } 143 | 144 | JNIEXPORT void JNICALL Java_org_pngquant_Result_liq_1result_1destroy(JNIEnv *env, jclass class, jlong result) { 145 | return liq_result_destroy((liq_result*)result); 146 | } 147 | 148 | JNIEXPORT jbyteArray JNICALL Java_org_pngquant_Result_liq_1get_1palette(JNIEnv *env, jclass class, jlong result) { 149 | const liq_palette *pal = liq_get_palette((liq_result*)result); 150 | jbyteArray arr = (*env)->NewByteArray(env, pal->count * 4); 151 | int i; 152 | for(i=0; i < pal->count; i++) { 153 | (*env)->SetByteArrayRegion(env, arr, i*4, 4, ((jbyte*)&pal->entries[i])); 154 | } 155 | return arr; 156 | } 157 | -------------------------------------------------------------------------------- /pngquant/libimagequant.cs: -------------------------------------------------------------------------------- 1 | /* 2 | This is an example demonstrating use of libimagequant from C#. 3 | 4 | This example code can be freely copied under CC0 (public domain) license. 5 | */ 6 | 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Runtime.InteropServices; 10 | 11 | [StructLayout(LayoutKind.Sequential)] 12 | struct liq_color 13 | { 14 | public byte r, g, b, a; 15 | }; 16 | 17 | [StructLayout(LayoutKind.Sequential)] 18 | struct liq_palette 19 | { 20 | public int count; 21 | [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] 22 | public liq_color[] entries; 23 | }; 24 | 25 | enum liq_error 26 | { 27 | LIQ_OK = 0, 28 | LIQ_QUALITY_TOO_LOW = 99, 29 | LIQ_VALUE_OUT_OF_RANGE = 100, 30 | LIQ_OUT_OF_MEMORY, 31 | LIQ_ABORTED, 32 | LIQ_BITMAP_NOT_AVAILABLE, 33 | LIQ_BUFFER_TOO_SMALL, 34 | LIQ_INVALID_POINTER, 35 | }; 36 | 37 | namespace liq 38 | { 39 | using liq_attr_ptr = IntPtr; 40 | using liq_image_ptr = IntPtr; 41 | using liq_result_ptr = IntPtr; 42 | using size_t = UIntPtr; 43 | 44 | class Liq 45 | { 46 | [DllImport(@"imagequant.dll")] 47 | public static extern liq_attr_ptr liq_attr_create(); 48 | [DllImport(@"imagequant.dll")] 49 | public static extern liq_attr_ptr liq_attr_copy(liq_attr_ptr attr); 50 | [DllImport(@"imagequant.dll")] 51 | public static extern void liq_attr_destroy(liq_attr_ptr attr); 52 | 53 | [DllImport(@"imagequant.dll")] 54 | public static extern liq_error liq_set_max_colors(liq_attr_ptr attr, int colors); 55 | [DllImport(@"imagequant.dll")] 56 | public static extern int liq_get_max_colors(liq_attr_ptr attr); 57 | [DllImport(@"imagequant.dll")] 58 | public static extern liq_error liq_set_speed(liq_attr_ptr attr, int speed); 59 | [DllImport(@"imagequant.dll")] 60 | public static extern int liq_get_speed(liq_attr_ptr attr); 61 | [DllImport(@"imagequant.dll")] 62 | public static extern liq_error liq_set_min_opacity(liq_attr_ptr attr, int min); 63 | [DllImport(@"imagequant.dll")] 64 | public static extern int liq_get_min_opacity(liq_attr_ptr attr); 65 | [DllImport(@"imagequant.dll")] 66 | public static extern liq_error liq_set_min_posterization(liq_attr_ptr attr, int bits); 67 | [DllImport(@"imagequant.dll")] 68 | public static extern int liq_get_min_posterization(liq_attr_ptr attr); 69 | [DllImport(@"imagequant.dll")] 70 | public static extern liq_error liq_set_quality(liq_attr_ptr attr, int minimum, int maximum); 71 | [DllImport(@"imagequant.dll")] 72 | public static extern int liq_get_min_quality(liq_attr_ptr attr); 73 | [DllImport(@"imagequant.dll")] 74 | public static extern int liq_get_max_quality(liq_attr_ptr attr); 75 | [DllImport(@"imagequant.dll")] 76 | public static extern void liq_set_last_index_transparent(liq_attr_ptr attr, int is_last); 77 | 78 | [DllImport(@"imagequant.dll")] 79 | public static extern liq_image_ptr liq_image_create_rgba(liq_attr_ptr attr, [In, MarshalAs(UnmanagedType.LPArray)] byte[] bitmap, int width, int height, double gamma); 80 | 81 | [DllImport(@"imagequant.dll")] 82 | public static extern liq_error liq_image_set_memory_ownership(liq_image_ptr image, int ownership_flags); 83 | [DllImport(@"imagequant.dll")] 84 | public static extern liq_error liq_image_add_fixed_color(liq_image_ptr img, liq_color color); 85 | [DllImport(@"imagequant.dll")] 86 | public static extern int liq_image_get_width(liq_image_ptr img); 87 | [DllImport(@"imagequant.dll")] 88 | public static extern int liq_image_get_height(liq_image_ptr img); 89 | [DllImport(@"imagequant.dll")] 90 | public static extern void liq_image_destroy(liq_image_ptr img); 91 | 92 | [DllImport(@"imagequant.dll")] 93 | public static extern liq_result_ptr liq_quantize_image(liq_attr_ptr attr, liq_image_ptr input_image); 94 | 95 | [DllImport(@"imagequant.dll")] 96 | public static extern liq_error liq_set_dithering_level(liq_result_ptr res, float dither_level); 97 | [DllImport(@"imagequant.dll")] 98 | public static extern liq_error liq_set_output_gamma(liq_result_ptr res, double gamma); 99 | [DllImport(@"imagequant.dll")] 100 | public static extern double liq_get_output_gamma(liq_result_ptr res); 101 | 102 | [DllImport(@"imagequant.dll")] 103 | public static extern IntPtr liq_get_palette(liq_result_ptr res); 104 | 105 | [DllImport(@"imagequant.dll")] 106 | public static extern liq_error liq_write_remapped_image(liq_result_ptr res, liq_image_ptr input_image, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] buffer, size_t buffer_size); 107 | 108 | [DllImport(@"imagequant.dll")] 109 | public static extern double liq_get_quantization_error(liq_result_ptr res); 110 | [DllImport(@"imagequant.dll")] 111 | public static extern int liq_get_quantization_quality(liq_result_ptr res); 112 | [DllImport(@"imagequant.dll")] 113 | public static extern double liq_get_remapping_error(liq_result_ptr res); 114 | [DllImport(@"imagequant.dll")] 115 | public static extern int liq_get_remapping_quality(liq_result_ptr res); 116 | 117 | [DllImport(@"imagequant.dll")] 118 | public static extern void liq_result_destroy(liq_result_ptr res); 119 | 120 | [DllImport(@"imagequant.dll")] 121 | public static extern int liq_version(); 122 | 123 | static void Main(string[] args) 124 | { 125 | Console.WriteLine("library version: {0}", liq_version()); 126 | 127 | int width = 3; 128 | int height = 1; 129 | 130 | var attr = liq_attr_create(); 131 | if (attr == IntPtr.Zero) throw new Exception("can't create attr"); 132 | 133 | byte[] bitmap = { // R, G, B, A, R, G, B, A, ... 134 | 111, 222, 33, 255, 135 | 255, 0, 255, 255, 136 | 255, 0, 255, 255, 137 | }; 138 | var img = liq_image_create_rgba(attr, bitmap, width, height, 0); 139 | if (img == IntPtr.Zero) throw new Exception("can't create image"); 140 | 141 | var res = liq_quantize_image(attr, img); 142 | if (res == IntPtr.Zero) throw new Exception("can't quantize image"); 143 | 144 | var buffer_size = width * height; 145 | var remapped = new byte[buffer_size]; 146 | 147 | var err = liq_write_remapped_image(res, img, remapped, (UIntPtr)buffer_size); 148 | if (err != liq_error.LIQ_OK) 149 | { 150 | throw new Exception("remapping error"); 151 | } 152 | 153 | Console.WriteLine("first pixel is {0}th palette entry", remapped[0]); 154 | 155 | liq_palette pal = (liq_palette)Marshal.PtrToStructure(liq_get_palette(res), typeof(liq_palette)); 156 | 157 | Console.WriteLine("palette entries: {0}; red of first entry: {1}", pal.count, pal.entries[0].r); 158 | 159 | liq_image_destroy(img); 160 | liq_result_destroy(res); 161 | liq_attr_destroy(attr); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /pngquant/nearest.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** © 2009-2015 by Kornel Lesiński. 3 | ** © 1989, 1991 by Jef Poskanzer. 4 | ** © 1997, 2000, 2002 by Greg Roelofs; based on an idea by Stefan Schneider. 5 | ** 6 | ** See COPYRIGHT file for license. 7 | */ 8 | 9 | #include "libimagequant.h" 10 | #include "pam.h" 11 | #include "nearest.h" 12 | #include "mempool.h" 13 | #include 14 | 15 | typedef struct vp_sort_tmp { 16 | float distance_squared; 17 | unsigned int idx; 18 | } vp_sort_tmp; 19 | 20 | typedef struct vp_search_tmp { 21 | float distance; 22 | unsigned int idx; 23 | int exclude; 24 | } vp_search_tmp; 25 | 26 | typedef struct vp_node { 27 | struct vp_node *near, *far; 28 | f_pixel vantage_point; 29 | float radius; 30 | unsigned int idx; 31 | } vp_node; 32 | 33 | struct nearest_map { 34 | vp_node *root; 35 | const colormap_item *palette; 36 | float nearest_other_color_dist[256]; 37 | mempoolptr mempool; 38 | }; 39 | 40 | static void vp_search_node(const vp_node *node, const f_pixel *const needle, vp_search_tmp *const best_candidate); 41 | 42 | static int vp_compare_distance(const void *ap, const void *bp) { 43 | float a = ((const vp_sort_tmp*)ap)->distance_squared; 44 | float b = ((const vp_sort_tmp*)bp)->distance_squared; 45 | return a > b ? 1 : -1; 46 | } 47 | 48 | static void vp_sort_indexes_by_distance(const f_pixel vantage_point, vp_sort_tmp indexes[], int num_indexes, const colormap_item items[]) { 49 | for(int i=0; i < num_indexes; i++) { 50 | indexes[i].distance_squared = colordifference(vantage_point, items[indexes[i].idx].acolor); 51 | } 52 | qsort(indexes, num_indexes, sizeof(indexes[0]), vp_compare_distance); 53 | } 54 | 55 | /* 56 | * Usually it should pick farthest point, but picking most popular point seems to make search quicker anyway 57 | */ 58 | static int vp_find_best_vantage_point_index(vp_sort_tmp indexes[], int num_indexes, const colormap_item items[]) { 59 | int best = 0; 60 | float best_popularity = items[indexes[0].idx].popularity; 61 | for(int i = 1; i < num_indexes; i++) { 62 | if (items[indexes[i].idx].popularity > best_popularity) { 63 | best_popularity = items[indexes[i].idx].popularity; 64 | best = i; 65 | } 66 | } 67 | return best; 68 | } 69 | 70 | static vp_node *vp_create_node(mempoolptr *m, vp_sort_tmp indexes[], int num_indexes, const colormap_item items[]) { 71 | if (num_indexes <= 0) { 72 | return NULL; 73 | } 74 | 75 | vp_node *node = mempool_alloc(m, sizeof(node[0]), 0); 76 | 77 | if (num_indexes == 1) { 78 | *node = (vp_node){ 79 | .vantage_point = items[indexes[0].idx].acolor, 80 | .idx = indexes[0].idx, 81 | .radius = MAX_DIFF, 82 | }; 83 | return node; 84 | } 85 | 86 | const int ref = vp_find_best_vantage_point_index(indexes, num_indexes, items); 87 | const int ref_idx = indexes[ref].idx; 88 | 89 | // Removes the `ref_idx` item from remaining items, because it's included in the current node 90 | num_indexes -= 1; 91 | indexes[ref] = indexes[num_indexes]; 92 | 93 | vp_sort_indexes_by_distance(items[ref_idx].acolor, indexes, num_indexes, items); 94 | 95 | // Remaining items are split by the median distance 96 | const int half_idx = num_indexes/2; 97 | 98 | *node = (vp_node){ 99 | .vantage_point = items[ref_idx].acolor, 100 | .idx = ref_idx, 101 | .radius = sqrtf(indexes[half_idx].distance_squared), 102 | }; 103 | node->near = vp_create_node(m, indexes, half_idx, items); 104 | node->far = vp_create_node(m, &indexes[half_idx], num_indexes - half_idx, items); 105 | 106 | return node; 107 | } 108 | 109 | LIQ_PRIVATE struct nearest_map *nearest_init(const colormap *map) { 110 | mempoolptr m = NULL; 111 | struct nearest_map *handle = mempool_create(&m, sizeof(handle[0]), sizeof(handle[0]) + sizeof(vp_node)*map->colors+16, map->malloc, map->free); 112 | 113 | LIQ_ARRAY(vp_sort_tmp, indexes, map->colors); 114 | 115 | for(unsigned int i=0; i < map->colors; i++) { 116 | indexes[i].idx = i; 117 | } 118 | 119 | vp_node *root = vp_create_node(&m, indexes, map->colors, map->palette); 120 | *handle = (struct nearest_map){ 121 | .root = root, 122 | .palette = map->palette, 123 | .mempool = m, 124 | }; 125 | 126 | for(unsigned int i=0; i < map->colors; i++) { 127 | vp_search_tmp best = { 128 | .distance = MAX_DIFF, 129 | .exclude = i, 130 | }; 131 | vp_search_node(root, &map->palette[i].acolor, &best); 132 | handle->nearest_other_color_dist[i] = best.distance * best.distance / 4.0; // half of squared distance 133 | } 134 | 135 | return handle; 136 | } 137 | 138 | static void vp_search_node(const vp_node *node, const f_pixel *const needle, vp_search_tmp *const best_candidate) { 139 | do { 140 | const float distance = sqrtf(colordifference(node->vantage_point, *needle)); 141 | 142 | if (distance < best_candidate->distance && best_candidate->exclude != node->idx) { 143 | best_candidate->distance = distance; 144 | best_candidate->idx = node->idx; 145 | } 146 | 147 | // Recurse towards most likely candidate first to narrow best candidate's distance as soon as possible 148 | if (distance < node->radius) { 149 | if (node->near) { 150 | vp_search_node(node->near, needle, best_candidate); 151 | } 152 | // The best node (final answer) may be just ouside the radius, but not farther than 153 | // the best distance we know so far. The vp_search_node above should have narrowed 154 | // best_candidate->distance, so this path is rarely taken. 155 | if (node->far && distance >= node->radius - best_candidate->distance) { 156 | node = node->far; // Fast tail recursion 157 | } else { 158 | break; 159 | } 160 | } else { 161 | if (node->far) { 162 | vp_search_node(node->far, needle, best_candidate); 163 | } 164 | if (node->near && distance <= node->radius + best_candidate->distance) { 165 | node = node->near; // Fast tail recursion 166 | } else { 167 | break; 168 | } 169 | } 170 | } while(true); 171 | } 172 | 173 | LIQ_PRIVATE unsigned int nearest_search(const struct nearest_map *handle, const f_pixel *px, const int likely_colormap_index, float *diff) { 174 | const float guess_diff = colordifference(handle->palette[likely_colormap_index].acolor, *px); 175 | if (guess_diff < handle->nearest_other_color_dist[likely_colormap_index]) { 176 | if (diff) *diff = guess_diff; 177 | return likely_colormap_index; 178 | } 179 | 180 | vp_search_tmp best_candidate = { 181 | .distance = sqrtf(guess_diff), 182 | .idx = likely_colormap_index, 183 | .exclude = -1, 184 | }; 185 | vp_search_node(handle->root, px, &best_candidate); 186 | if (diff) { 187 | *diff = best_candidate.distance * best_candidate.distance; 188 | } 189 | return best_candidate.idx; 190 | } 191 | 192 | LIQ_PRIVATE void nearest_free(struct nearest_map *centroids) 193 | { 194 | mempool_destroy(centroids->mempool); 195 | } 196 | -------------------------------------------------------------------------------- /pngquant/libimagequant.h: -------------------------------------------------------------------------------- 1 | /* 2 | * https://pngquant.org 3 | */ 4 | 5 | #ifndef LIBIMAGEQUANT_H 6 | #define LIBIMAGEQUANT_H 7 | 8 | #ifdef IMAGEQUANT_EXPORTS 9 | #define LIQ_EXPORT __declspec(dllexport) 10 | #endif 11 | 12 | #ifndef LIQ_EXPORT 13 | #define LIQ_EXPORT extern 14 | #endif 15 | 16 | #define LIQ_VERSION 21205 17 | #define LIQ_VERSION_STRING "2.12.5" 18 | 19 | #ifndef LIQ_PRIVATE 20 | #if defined(__GNUC__) || defined (__llvm__) 21 | #define LIQ_PRIVATE __attribute__((visibility("hidden"))) 22 | #define LIQ_NONNULL __attribute__((nonnull)) 23 | #define LIQ_USERESULT __attribute__((warn_unused_result)) 24 | #else 25 | #define LIQ_PRIVATE 26 | #define LIQ_NONNULL 27 | #define LIQ_USERESULT 28 | #endif 29 | #endif 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | #include 36 | 37 | typedef struct liq_attr liq_attr; 38 | typedef struct liq_image liq_image; 39 | typedef struct liq_result liq_result; 40 | typedef struct liq_histogram liq_histogram; 41 | 42 | typedef struct liq_color { 43 | unsigned char r, g, b, a; 44 | } liq_color; 45 | 46 | typedef struct liq_palette { 47 | unsigned int count; 48 | liq_color entries[256]; 49 | } liq_palette; 50 | 51 | typedef enum liq_error { 52 | LIQ_OK = 0, 53 | LIQ_QUALITY_TOO_LOW = 99, 54 | LIQ_VALUE_OUT_OF_RANGE = 100, 55 | LIQ_OUT_OF_MEMORY, 56 | LIQ_ABORTED, 57 | LIQ_BITMAP_NOT_AVAILABLE, 58 | LIQ_BUFFER_TOO_SMALL, 59 | LIQ_INVALID_POINTER, 60 | LIQ_UNSUPPORTED, 61 | } liq_error; 62 | 63 | enum liq_ownership { 64 | LIQ_OWN_ROWS=4, 65 | LIQ_OWN_PIXELS=8, 66 | LIQ_COPY_PIXELS=16, 67 | }; 68 | 69 | typedef struct liq_histogram_entry { 70 | liq_color color; 71 | unsigned int count; 72 | } liq_histogram_entry; 73 | 74 | LIQ_EXPORT LIQ_USERESULT liq_attr* liq_attr_create(void); 75 | LIQ_EXPORT LIQ_USERESULT liq_attr* liq_attr_create_with_allocator(void* (*malloc)(size_t), void (*free)(void*)); 76 | LIQ_EXPORT LIQ_USERESULT liq_attr* liq_attr_copy(const liq_attr *orig) LIQ_NONNULL; 77 | LIQ_EXPORT void liq_attr_destroy(liq_attr *attr) LIQ_NONNULL; 78 | 79 | LIQ_EXPORT LIQ_USERESULT liq_histogram* liq_histogram_create(const liq_attr* attr); 80 | LIQ_EXPORT liq_error liq_histogram_add_image(liq_histogram *hist, const liq_attr *attr, liq_image* image) LIQ_NONNULL; 81 | LIQ_EXPORT liq_error liq_histogram_add_colors(liq_histogram *hist, const liq_attr *attr, const liq_histogram_entry entries[], int num_entries, double gamma) LIQ_NONNULL; 82 | LIQ_EXPORT liq_error liq_histogram_add_fixed_color(liq_histogram *hist, liq_color color, double gamma) LIQ_NONNULL; 83 | LIQ_EXPORT void liq_histogram_destroy(liq_histogram *hist) LIQ_NONNULL; 84 | 85 | LIQ_EXPORT liq_error liq_set_max_colors(liq_attr* attr, int colors) LIQ_NONNULL; 86 | LIQ_EXPORT LIQ_USERESULT int liq_get_max_colors(const liq_attr* attr) LIQ_NONNULL; 87 | LIQ_EXPORT liq_error liq_set_speed(liq_attr* attr, int speed) LIQ_NONNULL; 88 | LIQ_EXPORT LIQ_USERESULT int liq_get_speed(const liq_attr* attr) LIQ_NONNULL; 89 | LIQ_EXPORT liq_error liq_set_min_opacity(liq_attr* attr, int min) LIQ_NONNULL; 90 | LIQ_EXPORT LIQ_USERESULT int liq_get_min_opacity(const liq_attr* attr) LIQ_NONNULL; 91 | LIQ_EXPORT liq_error liq_set_min_posterization(liq_attr* attr, int bits) LIQ_NONNULL; 92 | LIQ_EXPORT LIQ_USERESULT int liq_get_min_posterization(const liq_attr* attr) LIQ_NONNULL; 93 | LIQ_EXPORT liq_error liq_set_quality(liq_attr* attr, int minimum, int maximum) LIQ_NONNULL; 94 | LIQ_EXPORT LIQ_USERESULT int liq_get_min_quality(const liq_attr* attr) LIQ_NONNULL; 95 | LIQ_EXPORT LIQ_USERESULT int liq_get_max_quality(const liq_attr* attr) LIQ_NONNULL; 96 | LIQ_EXPORT void liq_set_last_index_transparent(liq_attr* attr, int is_last) LIQ_NONNULL; 97 | 98 | typedef void liq_log_callback_function(const liq_attr*, const char *message, void* user_info); 99 | typedef void liq_log_flush_callback_function(const liq_attr*, void* user_info); 100 | LIQ_EXPORT void liq_set_log_callback(liq_attr*, liq_log_callback_function*, void* user_info); 101 | LIQ_EXPORT void liq_set_log_flush_callback(liq_attr*, liq_log_flush_callback_function*, void* user_info); 102 | 103 | typedef int liq_progress_callback_function(float progress_percent, void* user_info); 104 | LIQ_EXPORT void liq_attr_set_progress_callback(liq_attr*, liq_progress_callback_function*, void* user_info); 105 | LIQ_EXPORT void liq_result_set_progress_callback(liq_result*, liq_progress_callback_function*, void* user_info); 106 | 107 | // The rows and their data are not modified. The type of `rows` is non-const only due to a bug in C's typesystem design. 108 | LIQ_EXPORT LIQ_USERESULT liq_image *liq_image_create_rgba_rows(const liq_attr *attr, void *const rows[], int width, int height, double gamma) LIQ_NONNULL; 109 | LIQ_EXPORT LIQ_USERESULT liq_image *liq_image_create_rgba(const liq_attr *attr, const void *bitmap, int width, int height, double gamma) LIQ_NONNULL; 110 | 111 | typedef void liq_image_get_rgba_row_callback(liq_color row_out[], int row, int width, void* user_info); 112 | LIQ_EXPORT LIQ_USERESULT liq_image *liq_image_create_custom(const liq_attr *attr, liq_image_get_rgba_row_callback *row_callback, void* user_info, int width, int height, double gamma); 113 | 114 | LIQ_EXPORT liq_error liq_image_set_memory_ownership(liq_image *image, int ownership_flags) LIQ_NONNULL; 115 | LIQ_EXPORT liq_error liq_image_set_background(liq_image *img, liq_image *background_image) LIQ_NONNULL; 116 | LIQ_EXPORT liq_error liq_image_set_importance_map(liq_image *img, unsigned char buffer[], size_t buffer_size, enum liq_ownership memory_handling) LIQ_NONNULL; 117 | LIQ_EXPORT liq_error liq_image_add_fixed_color(liq_image *img, liq_color color) LIQ_NONNULL; 118 | LIQ_EXPORT LIQ_USERESULT int liq_image_get_width(const liq_image *img) LIQ_NONNULL; 119 | LIQ_EXPORT LIQ_USERESULT int liq_image_get_height(const liq_image *img) LIQ_NONNULL; 120 | LIQ_EXPORT void liq_image_destroy(liq_image *img) LIQ_NONNULL; 121 | 122 | LIQ_EXPORT LIQ_USERESULT liq_error liq_histogram_quantize(liq_histogram *const input_hist, liq_attr *const options, liq_result **result_output) LIQ_NONNULL; 123 | LIQ_EXPORT LIQ_USERESULT liq_error liq_image_quantize(liq_image *const input_image, liq_attr *const options, liq_result **result_output) LIQ_NONNULL; 124 | 125 | LIQ_EXPORT liq_error liq_set_dithering_level(liq_result *res, float dither_level) LIQ_NONNULL; 126 | LIQ_EXPORT liq_error liq_set_output_gamma(liq_result* res, double gamma) LIQ_NONNULL; 127 | LIQ_EXPORT LIQ_USERESULT double liq_get_output_gamma(const liq_result *result) LIQ_NONNULL; 128 | 129 | LIQ_EXPORT LIQ_USERESULT const liq_palette *liq_get_palette(liq_result *result) LIQ_NONNULL; 130 | 131 | LIQ_EXPORT liq_error liq_write_remapped_image(liq_result *result, liq_image *input_image, void *buffer, size_t buffer_size) LIQ_NONNULL; 132 | LIQ_EXPORT liq_error liq_write_remapped_image_rows(liq_result *result, liq_image *input_image, unsigned char **row_pointers) LIQ_NONNULL; 133 | 134 | LIQ_EXPORT double liq_get_quantization_error(const liq_result *result) LIQ_NONNULL; 135 | LIQ_EXPORT int liq_get_quantization_quality(const liq_result *result) LIQ_NONNULL; 136 | LIQ_EXPORT double liq_get_remapping_error(const liq_result *result) LIQ_NONNULL; 137 | LIQ_EXPORT int liq_get_remapping_quality(const liq_result *result) LIQ_NONNULL; 138 | 139 | LIQ_EXPORT void liq_result_destroy(liq_result *) LIQ_NONNULL; 140 | 141 | LIQ_EXPORT int liq_version(void); 142 | 143 | 144 | // Deprecated 145 | LIQ_EXPORT LIQ_USERESULT liq_result *liq_quantize_image(liq_attr *options, liq_image *input_image) LIQ_NONNULL; 146 | 147 | #ifdef __cplusplus 148 | } 149 | #endif 150 | 151 | #endif 152 | -------------------------------------------------------------------------------- /pngquant/configure: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CONFIG="config.mk" 4 | PREFIX="/usr/local" 5 | LIBDIR="$PREFIX/lib" 6 | INCLUDEDIR="$PREFIX/include" 7 | PKGCONFIGDIR="$LIBDIR/pkgconfig" 8 | VERSION=$(grep LIQ_VERSION_STRING libimagequant.h | grep -Eo "2\.[0-9.]+") 9 | 10 | DEBUG= 11 | QUIET=0 12 | SSE=auto 13 | OPENMP= 14 | EXTRA_CFLAGS= 15 | EXTRA_LDFLAGS= 16 | 17 | # make gcc default compiler unless CC is already set 18 | CC=${CC:-gcc} 19 | 20 | help() { 21 | printf "%4s %s\n" "" "$1" 22 | } 23 | 24 | for i in "$@"; do 25 | case $i in 26 | --help|-h) 27 | echo 28 | help "--prefix= installation directory [$PREFIX]" 29 | help "--libdir= installation directory [$LIBDIR]" 30 | help "--includedir= installation directory [$INCLUDEDIR]" 31 | help "--pkgconfigdir= installation directory [$PKGCONFIGDIR]" 32 | help "--extra-cflags= append to CFLAGS" 33 | help "--extra-ldflags= append to LDFLAGS" 34 | echo 35 | help "--enable-debug" 36 | help "--enable-sse/--disable-sse enable/disable SSE instructions" 37 | echo 38 | help "--with-openmp=static compile with multicore support" 39 | echo 40 | help "CC= use given compiler command" 41 | help "CFLAGS= pass options to the compiler" 42 | help "LDFLAGS= pass options to the linker" 43 | echo 44 | exit 0 45 | ;; 46 | # Can be set before or after configure. Latter overrides former. 47 | CC=*) 48 | CC=${i#*=} 49 | ;; 50 | CFLAGS=*) 51 | CFLAGS=${i#*=} 52 | ;; 53 | LDFLAGS=*) 54 | LDFLAGS=${i#*=} 55 | ;; 56 | --enable-debug) 57 | DEBUG=1 58 | ;; 59 | --enable-sse) 60 | SSE=1 61 | ;; 62 | --disable-sse) 63 | SSE=0 64 | ;; 65 | --quiet) 66 | QUIET=1 67 | ;; 68 | '') 69 | # allows a bash quirk 70 | ;; 71 | --with-openmp) 72 | OPENMP=1 73 | ;; 74 | --with-openmp=static) 75 | OPENMP=static 76 | ;; 77 | --prefix=*) 78 | PREFIX=${i#*=} 79 | LIBDIR="$PREFIX/lib" 80 | INCLUDEDIR="$PREFIX/include" 81 | PKGCONFIGDIR="$LIBDIR/pkgconfig" 82 | ;; 83 | --libdir=*) 84 | LIBDIR=${i#*=} 85 | PKGCONFIGDIR="$LIBDIR/pkgconfig" 86 | ;; 87 | --includedir=*) 88 | INCLUDEDIR=${i#*=} 89 | ;; 90 | --pkgconfigdir=*) 91 | PKGCONFIGDIR=${i#*=} 92 | ;; 93 | # can be used multiple times or in quotes to set multiple flags 94 | --extra-cflags=*) 95 | EXTRA_CFLAGS="$EXTRA_CFLAGS ${i#*=}" 96 | ;; 97 | --extra-ldflags=*) 98 | EXTRA_LDFLAGS="$EXTRA_LDFLAGS ${i#*=}" 99 | ;; 100 | *) 101 | echo "warning: unknown switch '${i%%=*}' (see $0 --help for the list)" 102 | ;; 103 | esac 104 | done 105 | 106 | # If someone runs sudo make install as very first command, and configure later, 107 | # $CONFIG cannot be overwritten, and must be deleted before continuing. 108 | if [[ -f "$CONFIG" && ! -w "$CONFIG" ]]; then 109 | echo "Cannot overwrite file $CONFIG! Please delete it." 110 | exit 1 111 | fi 112 | 113 | cflags() { 114 | CFLAGS="$CFLAGS $1" 115 | } 116 | 117 | lflags() { 118 | LDFLAGS="$LDFLAGS $1" 119 | } 120 | 121 | status() { 122 | if [ "$QUIET" -ne 1 ]; then 123 | printf "%10s: %s\n" "$1" "$2" 124 | fi 125 | } 126 | 127 | # Append to CFLAGS if compiler supports flag, with optional prerequisite. 128 | # Fails on errors and warnings. 129 | conditional_cflags() { 130 | if [ -z "$(echo | "$CC" -xc -S -o /dev/null $2 $1 - 2>&1)" ]; then 131 | cflags "$1" 132 | fi 133 | } 134 | 135 | error() { 136 | status "$1" "error ... $2" 137 | echo 138 | exit 1 139 | } 140 | 141 | if [ "$QUIET" -ne 1 ]; then 142 | echo 143 | fi 144 | 145 | echo > pngquant-gcccheck.c "int main(){}" 146 | if ! "$CC" -std=c99 -o pngquant-gcccheck pngquant-gcccheck.c; then 147 | rm -f pngquant-gcccheck pngquant-gcccheck.c 148 | error "Compiler" "$CC failed to compile anything (make sure it's installed and supports C99)" 149 | fi 150 | rm -f pngquant-gcccheck pngquant-gcccheck.c 151 | 152 | status "Compiler" "$CC" 153 | 154 | # init flags 155 | CFLAGS=${CFLAGS:--fno-math-errno -funroll-loops -fomit-frame-pointer -Wall} 156 | cflags "-std=c99 -I." 157 | 158 | # DEBUG 159 | if [ -z "$DEBUG" ]; then 160 | cflags "-O3 -DNDEBUG" 161 | status "Debug" "no" 162 | else 163 | cflags "-O1 -g -DDEBUG" 164 | status "Debug" "yes" 165 | fi 166 | 167 | # SSE 168 | if [ "$SSE" = 'auto' ]; then 169 | SSE=0 170 | if type uname > /dev/null; then 171 | if [[ "$(uname -m)" =~ "amd64" || "$(uname -m)" =~ "x86_64" || 172 | "$(grep -E -m1 "^flags" /proc/cpuinfo)" =~ "sse" ]]; then 173 | SSE=1 174 | fi 175 | fi 176 | fi 177 | 178 | if [ "$SSE" -eq 1 ]; then 179 | status "SSE" "yes" 180 | cflags "-DUSE_SSE=1" 181 | cflags "-msse" 182 | # Silence a later ICC warning due to -msse working slightly different. 183 | conditional_cflags "-wd10121" 184 | # Must be set explicitly for GCC on x86_32. Other compilers imply it. 185 | conditional_cflags "-mfpmath=sse" "-msse" 186 | elif [ "$SSE" -eq 0 ]; then 187 | status "SSE" "no" 188 | cflags "-DUSE_SSE=0" 189 | fi 190 | 191 | # OpenMP 192 | if [ -n "$OPENMP" ]; then 193 | if [ "static" = "$OPENMP" ]; then 194 | OPENMPFLAGS="-static-libgcc -Bstatic -fopenmp -Bdynamic" 195 | else 196 | OPENMPFLAGS="-fopenmp" 197 | fi 198 | if [[ "$("$CC" -xc -E $OPENMPFLAGS <(echo "#ifdef _OPENMP 199 | #include 200 | #endif") 2>&1)" =~ "omp_get_thread_num" ]]; then 201 | cflags "$OPENMPFLAGS" 202 | lflags "$OPENMPFLAGS" 203 | status "OpenMP" "yes" 204 | else 205 | error "OpenMP" "not supported by compiler (please install a compiler that supports OpenMP (e.g. gcc) and specify it with the CC= argument)" 206 | fi 207 | else 208 | # silence warnings about omp pragmas 209 | cflags "-Wno-unknown-pragmas" 210 | conditional_cflags "-wd3180" # ICC 211 | status "OpenMP" "no" 212 | fi 213 | 214 | # Cocoa 215 | if [[ "$OSTYPE" =~ "darwin" ]]; then 216 | cflags "-mmacosx-version-min=10.7" 217 | lflags "-mmacosx-version-min=10.7" 218 | fi 219 | 220 | if [[ "$OSTYPE" =~ "darwin" ]]; then 221 | SOLIBSUFFIX=dylib 222 | 223 | # Search Developer SDK paths, since Apple seems to have dropped the standard Unixy ones 224 | XCODE_CMD="xcode-select" 225 | XCODE_PATH=$($XCODE_CMD -p) 226 | DIRS+=("$XCODE_PATH/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/include $XCODE_PATH/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/usr/lib") 227 | DIRS+=("$XCODE_PATH/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/usr/include $XCODE_PATH/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/usr/lib") 228 | elif [[ "$OSTYPE" =~ "msys" ]]; then 229 | SOLIBSUFFIX=dll 230 | else 231 | SOLIBSUFFIX=so 232 | fi 233 | 234 | if [ "$QUIET" -ne 1 ]; then 235 | echo 236 | fi 237 | 238 | # As of GCC 4.5, 387 fp math is significantly slower in C99 mode without this. 239 | # Note: CPUs without SSE2 use 387 for doubles, even when SSE fp math is set. 240 | conditional_cflags "-fexcess-precision=fast" 241 | 242 | # Intel C++ Compiler 243 | 244 | # ICC does usually only produce fast(er) code when it can optimize to the full 245 | # capabilites of the (Intel) CPU. This is equivalent to -march=native for GCC. 246 | conditional_cflags "-xHOST" 247 | 248 | # Disable unsafe fp optimizations and enforce fp precision as set in the source. 249 | conditional_cflags "-fp-model source" 250 | 251 | # Silence a gold linker warning about string misalignment. 252 | conditional_cflags "-falign-stack=maintain-16-byte" 253 | 254 | lflags "-lm" # Ubuntu requires this library last, issue #38 255 | 256 | if [ -n "$EXTRA_CFLAGS" ]; then 257 | cflags "$EXTRA_CFLAGS" 258 | fi 259 | 260 | if [ -n "$EXTRA_LDFLAGS" ]; then 261 | lflags "$EXTRA_LDFLAGS" 262 | fi 263 | 264 | # Overwrite previous configuration. 265 | echo " 266 | # auto-generated by configure 267 | PREFIX = $PREFIX 268 | LIBDIR = $LIBDIR 269 | INCLUDEDIR = $INCLUDEDIR 270 | PKGCONFIGDIR = $PKGCONFIGDIR 271 | VERSION = $VERSION 272 | CC = $CC 273 | CFLAGS = $CFLAGS 274 | LDFLAGS = $LDFLAGS 275 | SOLIBSUFFIX = $SOLIBSUFFIX 276 | " > "$CONFIG" 277 | -------------------------------------------------------------------------------- /pngquant/pam.h: -------------------------------------------------------------------------------- 1 | /* pam.h - pam (portable alpha map) utility library 2 | ** 3 | ** Colormap routines. 4 | ** 5 | ** Copyright (C) 1989, 1991 by Jef Poskanzer. 6 | ** Copyright (C) 1997 by Greg Roelofs. 7 | ** 8 | ** Permission to use, copy, modify, and distribute this software and its 9 | ** documentation for any purpose and without fee is hereby granted, provided 10 | ** that the above copyright notice appear in all copies and that both that 11 | ** copyright notice and this permission notice appear in supporting 12 | ** documentation. This software is provided "as is" without express or 13 | ** implied warranty. 14 | */ 15 | 16 | #ifndef PAM_H 17 | #define PAM_H 18 | 19 | // accidental debug assertions make color search much slower, 20 | // so force assertions off if there's no explicit setting 21 | #if !defined(NDEBUG) && !defined(DEBUG) 22 | #define NDEBUG 23 | #endif 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #ifndef MAX 31 | # define MAX(a,b) ((a) > (b)? (a) : (b)) 32 | # define MIN(a,b) ((a) < (b)? (a) : (b)) 33 | #endif 34 | 35 | #define MAX_DIFF 1e20 36 | 37 | #ifndef USE_SSE 38 | # if defined(__SSE__) && (defined(__amd64__) || defined(__X86_64__) || defined(_WIN64) || defined(WIN32) || defined(__WIN32__)) 39 | # define USE_SSE 1 40 | # else 41 | # define USE_SSE 0 42 | # endif 43 | #endif 44 | 45 | #if USE_SSE 46 | # include 47 | # ifdef _MSC_VER 48 | # include 49 | # define SSE_ALIGN 50 | # else 51 | # define SSE_ALIGN __attribute__ ((aligned (16))) 52 | # if defined(__i386__) && defined(__PIC__) 53 | # define cpuid(func,ax,bx,cx,dx)\ 54 | __asm__ __volatile__ ( \ 55 | "push %%ebx\n" \ 56 | "cpuid\n" \ 57 | "mov %%ebx, %1\n" \ 58 | "pop %%ebx\n" \ 59 | : "=a" (ax), "=r" (bx), "=c" (cx), "=d" (dx) \ 60 | : "a" (func)); 61 | # else 62 | # define cpuid(func,ax,bx,cx,dx)\ 63 | __asm__ __volatile__ ("cpuid":\ 64 | "=a" (ax), "=b" (bx), "=c" (cx), "=d" (dx) : "a" (func)); 65 | # endif 66 | #endif 67 | #else 68 | # define SSE_ALIGN 69 | #endif 70 | 71 | #ifndef _MSC_VER 72 | #define LIQ_ARRAY(type, var, count) type var[count] 73 | #else 74 | #define LIQ_ARRAY(type, var, count) type* var = (type*)_alloca(sizeof(type)*(count)) 75 | #endif 76 | 77 | #if defined(__GNUC__) || defined (__llvm__) 78 | #define ALWAYS_INLINE __attribute__((always_inline)) inline 79 | #define NEVER_INLINE __attribute__ ((noinline)) 80 | #elif defined(_MSC_VER) 81 | #define inline __inline 82 | #define restrict __restrict 83 | #define ALWAYS_INLINE __forceinline 84 | #define NEVER_INLINE __declspec(noinline) 85 | #else 86 | #define ALWAYS_INLINE inline 87 | #define NEVER_INLINE 88 | #endif 89 | 90 | /* from pam.h */ 91 | 92 | typedef struct { 93 | unsigned char r, g, b, a; 94 | } rgba_pixel; 95 | 96 | typedef struct { 97 | float a, r, g, b; 98 | } SSE_ALIGN f_pixel; 99 | 100 | static const float internal_gamma = 0.5499f; 101 | 102 | LIQ_PRIVATE void to_f_set_gamma(float gamma_lut[], const double gamma); 103 | 104 | /** 105 | Converts 8-bit color to internal gamma and premultiplied alpha. 106 | (premultiplied color space is much better for blending of semitransparent colors) 107 | */ 108 | ALWAYS_INLINE static f_pixel rgba_to_f(const float gamma_lut[], const rgba_pixel px); 109 | inline static f_pixel rgba_to_f(const float gamma_lut[], const rgba_pixel px) 110 | { 111 | float a = px.a/255.f; 112 | 113 | return (f_pixel) { 114 | .a = a, 115 | .r = gamma_lut[px.r]*a, 116 | .g = gamma_lut[px.g]*a, 117 | .b = gamma_lut[px.b]*a, 118 | }; 119 | } 120 | 121 | inline static rgba_pixel f_to_rgb(const float gamma, const f_pixel px) 122 | { 123 | if (px.a < 1.f/256.f) { 124 | return (rgba_pixel){0,0,0,0}; 125 | } 126 | 127 | float r = px.r / px.a, 128 | g = px.g / px.a, 129 | b = px.b / px.a, 130 | a = px.a; 131 | 132 | r = powf(r, gamma/internal_gamma); 133 | g = powf(g, gamma/internal_gamma); 134 | b = powf(b, gamma/internal_gamma); 135 | 136 | // 256, because numbers are in range 1..255.9999… rounded down 137 | r *= 256.f; 138 | g *= 256.f; 139 | b *= 256.f; 140 | a *= 256.f; 141 | 142 | return (rgba_pixel){ 143 | .r = r>=255.f ? 255 : r, 144 | .g = g>=255.f ? 255 : g, 145 | .b = b>=255.f ? 255 : b, 146 | .a = a>=255.f ? 255 : a, 147 | }; 148 | } 149 | 150 | ALWAYS_INLINE static double colordifference_ch(const double x, const double y, const double alphas); 151 | inline static double colordifference_ch(const double x, const double y, const double alphas) 152 | { 153 | // maximum of channel blended on white, and blended on black 154 | // premultiplied alpha and backgrounds 0/1 shorten the formula 155 | const double black = x-y, white = black+alphas; 156 | return MAX(black*black, white*white); 157 | } 158 | 159 | ALWAYS_INLINE static float colordifference_stdc(const f_pixel px, const f_pixel py); 160 | inline static float colordifference_stdc(const f_pixel px, const f_pixel py) 161 | { 162 | // px_b.rgb = px.rgb + 0*(1-px.a) // blend px on black 163 | // px_b.a = px.a + 1*(1-px.a) 164 | // px_w.rgb = px.rgb + 1*(1-px.a) // blend px on white 165 | // px_w.a = px.a + 1*(1-px.a) 166 | 167 | // px_b.rgb = px.rgb // difference same as in opaque RGB 168 | // px_b.a = 1 169 | // px_w.rgb = px.rgb - px.a // difference simplifies to formula below 170 | // px_w.a = 1 171 | 172 | // (px.rgb - px.a) - (py.rgb - py.a) 173 | // (px.rgb - py.rgb) + (py.a - px.a) 174 | 175 | const double alphas = py.a-px.a; 176 | return colordifference_ch(px.r, py.r, alphas) + 177 | colordifference_ch(px.g, py.g, alphas) + 178 | colordifference_ch(px.b, py.b, alphas); 179 | } 180 | 181 | ALWAYS_INLINE static float colordifference(f_pixel px, f_pixel py); 182 | inline static float colordifference(f_pixel px, f_pixel py) 183 | { 184 | #if USE_SSE 185 | const __m128 vpx = _mm_load_ps((const float*)&px); 186 | const __m128 vpy = _mm_load_ps((const float*)&py); 187 | 188 | // y.a - x.a 189 | __m128 alphas = _mm_sub_ss(vpy, vpx); 190 | alphas = _mm_shuffle_ps(alphas,alphas,0); // copy first to all four 191 | 192 | __m128 onblack = _mm_sub_ps(vpx, vpy); // x - y 193 | __m128 onwhite = _mm_add_ps(onblack, alphas); // x - y + (y.a - x.a) 194 | 195 | onblack = _mm_mul_ps(onblack, onblack); 196 | onwhite = _mm_mul_ps(onwhite, onwhite); 197 | const __m128 max = _mm_max_ps(onwhite, onblack); 198 | 199 | // add rgb, not a 200 | const __m128 maxhl = _mm_movehl_ps(max, max); 201 | const __m128 tmp = _mm_add_ps(max, maxhl); 202 | const __m128 sum = _mm_add_ss(maxhl, _mm_shuffle_ps(tmp, tmp, 1)); 203 | 204 | const float res = _mm_cvtss_f32(sum); 205 | assert(fabs(res - colordifference_stdc(px,py)) < 0.001); 206 | return res; 207 | #else 208 | return colordifference_stdc(px,py); 209 | #endif 210 | } 211 | 212 | /* from pamcmap.h */ 213 | union rgba_as_int { 214 | rgba_pixel rgba; 215 | unsigned int l; 216 | }; 217 | 218 | typedef struct { 219 | f_pixel acolor; 220 | float adjusted_weight, // perceptual weight changed to tweak how mediancut selects colors 221 | perceptual_weight; // number of pixels weighted by importance of different areas of the picture 222 | 223 | float color_weight; // these two change every time histogram subset is sorted 224 | union { 225 | unsigned int sort_value; 226 | unsigned char likely_colormap_index; 227 | } tmp; 228 | } hist_item; 229 | 230 | typedef struct { 231 | hist_item *achv; 232 | void (*free)(void*); 233 | double total_perceptual_weight; 234 | unsigned int size; 235 | unsigned int ignorebits; 236 | } histogram; 237 | 238 | typedef struct { 239 | f_pixel acolor; 240 | float popularity; 241 | bool fixed; // if true it's user-supplied and must not be changed (e.g in K-Means iteration) 242 | } colormap_item; 243 | 244 | typedef struct colormap { 245 | unsigned int colors; 246 | void* (*malloc)(size_t); 247 | void (*free)(void*); 248 | colormap_item palette[]; 249 | } colormap; 250 | 251 | struct acolorhist_arr_item { 252 | union rgba_as_int color; 253 | unsigned int perceptual_weight; 254 | }; 255 | 256 | struct acolorhist_arr_head { 257 | struct acolorhist_arr_item inline1, inline2; 258 | unsigned int used, capacity; 259 | struct acolorhist_arr_item *other_items; 260 | }; 261 | 262 | struct acolorhash_table { 263 | struct mempool *mempool; 264 | unsigned int ignorebits, maxcolors, colors, cols, rows; 265 | unsigned int hash_size; 266 | unsigned int freestackp; 267 | struct acolorhist_arr_item *freestack[512]; 268 | struct acolorhist_arr_head buckets[]; 269 | }; 270 | 271 | LIQ_PRIVATE void pam_freeacolorhash(struct acolorhash_table *acht); 272 | LIQ_PRIVATE struct acolorhash_table *pam_allocacolorhash(unsigned int maxcolors, unsigned int surface, unsigned int ignorebits, void* (*malloc)(size_t), void (*free)(void*)); 273 | LIQ_PRIVATE histogram *pam_acolorhashtoacolorhist(const struct acolorhash_table *acht, const double gamma, void* (*malloc)(size_t), void (*free)(void*)); 274 | LIQ_PRIVATE bool pam_computeacolorhash(struct acolorhash_table *acht, const rgba_pixel *const pixels[], unsigned int cols, unsigned int rows, const unsigned char *importance_map); 275 | LIQ_PRIVATE bool pam_add_to_hash(struct acolorhash_table *acht, unsigned int hash, unsigned int boost, union rgba_as_int px, unsigned int row, unsigned int rows); 276 | 277 | LIQ_PRIVATE void pam_freeacolorhist(histogram *h); 278 | 279 | LIQ_PRIVATE colormap *pam_colormap(unsigned int colors, void* (*malloc)(size_t), void (*free)(void*)); 280 | LIQ_PRIVATE colormap *pam_duplicate_colormap(colormap *map); 281 | LIQ_PRIVATE void pam_freecolormap(colormap *c); 282 | 283 | #endif 284 | -------------------------------------------------------------------------------- /filedata-tool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import struct 4 | import os 5 | from decimal import * 6 | from sys import argv 7 | import sys 8 | import csv 9 | import argparse 10 | 11 | class FileData: 12 | # Length of the ELF is used as the key 13 | table_info = { 14 | 0x176C2C: { 15 | # Tested working 16 | 'name': 'MAX JP', 17 | 'offset': 0x175B18, 18 | 'num_entries': 374 19 | }, 20 | 0x19D568: { 21 | # Tested working 22 | 'name': 'MAX US', 23 | 'offset': 0x168320, 24 | 'num_entries': 577 25 | }, 26 | 0x1DAC88: { 27 | 'name': 'MAX 2 JP', 28 | 'offset': 0x17DDE8, 29 | 'num_entries': 675 30 | }, 31 | 0x265854: { 32 | 'name': 'MAX 2 US', 33 | 'offset': 0x1A0810, 34 | 'num_entries': 795 35 | }, 36 | 0x12A608: { 37 | 'name': 'MAX 2 E3 Demo US', 38 | 'offset': 0x1842F0, 39 | 'num_entries': 223 40 | }, 41 | 2672124: { 42 | # Tested working 43 | 'name': 'Extreme JP', 44 | 'offset': 0x1B3130, 45 | 'num_entries': 656 46 | }, 47 | 3871008: { 48 | 'name': 'Extreme E3 Demo US', 49 | 'offset': 0x17C880, 50 | 'num_entries': 680 51 | }, 52 | 2725576: { 53 | 'name': 'Party Collection JP', 54 | 'offset': 0x1A1548, 55 | 'num_entries': 459 56 | } 57 | } 58 | 59 | csv_fieldnames = ['id', 'offset', 'length', 'filename', 'description'] 60 | 61 | def _get_table_info(self, elf_path): 62 | length = os.path.getsize(elf_path) 63 | try: 64 | return self.table_info[length] 65 | except: 66 | return None 67 | 68 | def _guess_filetype(self, stream, length): 69 | ''' Guess the filetype of a binary stream 70 | 71 | Parameters: 72 | stream: file stream at start of data 73 | length: length of data 74 | 75 | Return value: 76 | Dictionary with description and extension keys. 77 | The file pointer will be returned to its original position. 78 | ''' 79 | 80 | if length > 4 and stream.peek(4)[:4] == b'Svag': 81 | return {'description': 'Konami PS2 SVAG Audio', 'extension': 'svag'} 82 | elif length > 4 and stream.peek(4)[:4] == b'ipum': 83 | return {'description': 'Sony PS2 IPU Video', 'extension': 'ipu'} 84 | elif length > 16 and stream.peek(16)[:16] == b'TCB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00': 85 | return {'description': 'Konami TCB Image', 'extension': 'tcb'} 86 | elif length > 13 and stream.peek(13)[4:13] == b'FrameInfo': 87 | return {'description': 'MPEG2 Stream', 'extension': 'mpeg2'} 88 | else: 89 | return {'description': 'Unknown Binary Data', 'extension': 'bin'} 90 | 91 | def _print_progress(self, iteration, total): 92 | ''' Print progress info to stdout ''' 93 | percent = int(round(100 * iteration/total, 0)) 94 | bar_filled = percent // 10 95 | bar = '#' * bar_filled + '-' * (10 - bar_filled) 96 | sys.stdout.write('\r[{}] {}%'.format(bar, percent)) 97 | sys.stdout.flush() 98 | 99 | def __init__(self, elf_path, filedata_path): 100 | self.elf_path = elf_path 101 | self.filedata_path = filedata_path 102 | self.table = [] 103 | self.table_info = self._get_table_info(elf_path) 104 | if self.table_info == None: 105 | raise LookupError('Unknown ELF; Cannot extract filetable.') 106 | 107 | def load_from_elf(self): 108 | ''' Read table contents from ELF file. ''' 109 | filedata_file = open(self.filedata_path, 'rb') 110 | with open(self.elf_path, 'rb') as elf_file: 111 | elf_file.seek(self.table_info['offset'], os.SEEK_SET) 112 | for _ in range(self.table_info['num_entries']): 113 | data = struct.unpack('