diff --git a/CMakeLists.txt b/CMakeLists.txt index 685f2f6e6..cdd0f2941 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,7 @@ cmake_dependent_option(ENABLE_ELF "Enable libelf" ON "LINUX OR ANDROID OR Dragon cmake_dependent_option(ENABLE_THREADS "Enable multithreading" ON "Threads_FOUND" OFF) option(ENABLE_ZLIB "Enable zlib" ON) +option(ENABLE_SIXEL "Enable sixel image logo output (vendored libsixel encoder)" ON) option(ENABLE_SYSTEM_YYJSON "Use system provided (instead of fastfetch embedded) yyjson library" OFF) option(ENABLE_ASAN "Build fastfetch with ASAN (address sanitizer)" OFF) option(ENABLE_TRACER "Build fastfetch with function tracing" OFF) @@ -1418,6 +1419,21 @@ else() ) endif() +if(ENABLE_SIXEL) + set(FF_SIXEL_SRC + src/3rdparty/sixel/allocator.c + src/3rdparty/sixel/dither.c + src/3rdparty/sixel/output.c + src/3rdparty/sixel/pixelformat.c + src/3rdparty/sixel/quant.c + src/3rdparty/sixel/status.c + src/3rdparty/sixel/tosixel.c + ) + list(APPEND LIBFASTFETCH_SRC ${FF_SIXEL_SRC}) + + set_source_files_properties(${FF_SIXEL_SRC} PROPERTIES COMPILE_OPTIONS "-Wno-conversion") +endif() + add_library(libfastfetch OBJECT ${LIBFASTFETCH_SRC} ) @@ -1993,6 +2009,12 @@ target_include_directories(libfastfetch PUBLIC ${PROJECT_SOURCE_DIR}/src ) +if(ENABLE_SIXEL) + # src/3rdparty/sixel/*.c include (angle brackets, so the directory itself + # has to be on the include path) + target_include_directories(libfastfetch PRIVATE ${PROJECT_SOURCE_DIR}/src/3rdparty/sixel) +endif() + target_link_libraries(libfastfetch PRIVATE ${CMAKE_DL_LIBS} ) diff --git a/src/3rdparty/sixel/LICENSE b/src/3rdparty/sixel/LICENSE new file mode 100644 index 000000000..3da7b4044 --- /dev/null +++ b/src/3rdparty/sixel/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2014-2016 Hayaki Saito + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/3rdparty/sixel/LICENSE.pnmcolormap b/src/3rdparty/sixel/LICENSE.pnmcolormap new file mode 100644 index 000000000..0b8ab89f7 --- /dev/null +++ b/src/3rdparty/sixel/LICENSE.pnmcolormap @@ -0,0 +1,13 @@ + + src/quant.c is derived from ppmquant, originally by Jef Poskanzer. + + Copyright (C) 1989, 1991 by Jef Poskanzer. + Copyright (C) 2001 by Bryan Henderson. + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, provided + that the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. This software is provided "as is" without express or + implied warranty. + diff --git a/src/3rdparty/sixel/LICENSE.sixel b/src/3rdparty/sixel/LICENSE.sixel new file mode 100644 index 000000000..862a32914 --- /dev/null +++ b/src/3rdparty/sixel/LICENSE.sixel @@ -0,0 +1,49 @@ +"sixel" license (original license) +================================== + +src/tosixel.c and src/fromsixel.c are derived from "sixel" original version (2014-3-2) + + Package: http://nanno.dip.jp/softlib/man/rlogin/sixel.tar.gz + + Unofficial repo: https://github.com/saitoha/sixel + +This work is written by kmiya@ culti. He distributes it under very permissive license. + +The original license text(in Japanese) is: + +``` +このプログラム及びソースコードの使用について個人・商用を問わず + +ご自由に使用していただいで結構です。 + +また、配布・転載・紹介もご連絡の必要もありません。 + +ソースの改変による配布も自由ですが、どのバージョンの改変かを + +明記されることを希望します。 + +バージョン情報が無い場合は、配布物の年月日を明記されることを + +希望します。 + + 2014/10/05 kmiya +``` + +The unofficial translation: + +``` +Anyone is free to use this program for any purpose, +commercial or non-commercial, without any restriction. + +Anyone is free to distribute, copy, publish, or +advertise this software, without any contact. + +Anyone is free to distribute with modification of the +source code, but I "hope" that its based version or +date will be written clearly. + + 2014/10/05 kmiya +``` + +kmiya also said this is compatible with MIT/BSD/GPL. + diff --git a/src/3rdparty/sixel/README.md b/src/3rdparty/sixel/README.md new file mode 100644 index 000000000..7550359a9 --- /dev/null +++ b/src/3rdparty/sixel/README.md @@ -0,0 +1,147 @@ +# libsixel (encoder subset, vendored) + +Encoder-only subset of [libsixel](https://github.com/saitoha/libsixel) `1.8.7-r2`, +used by `src/logo/image/` to render the `sixel` logo type **on Windows only**. + +## Why this is vendored + +Neither MSYS2 nor vcpkg packages libsixel, so there is nothing to `dlopen()` or to +link against. The encoder is small and has no external dependencies, so the +necessary files are embedded instead. The intent is to upstream a libsixel package +to MSYS2 later and switch to it; until then this directory is the source of truth. + +## Why Windows only + +Windows loses ImageMagick entirely — its image backend moves to WIC, which decodes +and scales but cannot *encode* sixel — so it needs an encoder of its own. + +On every other platform ImageMagick stays, and it already produces sixel through +its own SIXEL coder. Adding libsixel there would mean a second encoder for a +capability that already works: one more dependency for zero gain. So `ENABLE_SIXEL` +is gated on `WIN32`, and these sources are **not compiled at all** on Linux / macOS / +BSD. + +Consequence: sixel bytes differ between Windows (libsixel) and the rest +(ImageMagick). That is deliberate and accepted — see +`doc/windows-image-backend.md` decision 9. + +## What was kept + +Starting from `sixel_dither_new` / `sixel_dither_initialize` / `sixel_output_new` / +`sixel_encode` and following the dependency closure, only these files are reachable: + +| File | Provides | +|---|---| +| `tosixel.c` | `sixel_encode`, `sixel_putc`, `sixel_node_*`, DCS envelope handling | +| `dither.c` / `dither.h` | `sixel_dither_new`, `sixel_dither_initialize`, `sixel_dither_unref` | +| `output.c` / `output.h` | `sixel_output_new`, `sixel_output_unref` | +| `quant.c` / `quant.h` | Median-cut palette construction | +| `pixelformat.c` | `sixel_helper_compute_depth`, `sixel_helper_normalize_pixelformat` | +| `allocator.c` / `allocator.h` | `sixel_allocator_*` | +| `status.c` / `status.h` | `sixel_helper_set_additional_message` | +| `malloc_stub.h` | Included by `allocator.c`; compiles to nothing when `HAVE_MALLOC` | +| `sixel.h` | Public API (upstream `include/sixel.h`) | + +## What was removed + +Everything else in the upstream tree, in particular: + +- **Decoding**: `decoder.c`, `fromsixel.c`, `frompnm.c`, `fromgif.c`, `frame.c`, + `loader.c`, `stb_image.h`, `stb_image_write.*`. Image decoding is done by the + platform backend (WIC on Windows, ImageMagick elsewhere), so libsixel only ever + sees an RGBA8 buffer here. +- **The `sixel_encoder_*` / `sixel_decoder_*` high-level API**: `encoder.c`, + `writer.c`, `tty.c`, `scale.c`. These pull in file I/O, terminal probing and the + loader registry. We call the dither/output/encode trio directly. +- **`rgblookup.h` / `rgblookup.gperf`**: a gperf-generated color-name table used + only by `encoder.c` for `--builtin-palette` parsing. +- **`converters/`** (`img2sixel`, `sixel2png`), **`python/`**, **`tools/`**, + **`tests/`**, **`images/`** (216 test images), and the packaging templates + (`package.json.in.in`, `libsixel.pc.in`). +- **The autotools machinery**: `configure`, `Makefile.in`, `aclocal.m4`, `m4/`, + `ltmain.sh`, `config.h.in`, … See `config.h` below. +- **Six of the nine `LICENSE.*` files**: `LICENSE.images` / `.mesa` / `.pngsuite` / + `.sdump` / `.stb` cover only material that was dropped (test images, the OpenGL + example, the `sdump` tool, stb). The three that remain are the complete set + required by the files above — verified against each file's own header, see + [License](#license). + +Result: **20 files / 255 KB**, down from 319 files / 11.3 MB. Note that 8.0 MB of +that 11.3 MB was `images/` fixtures, so the meaningful comparison is code: +`src/` + `include/` went from 50 files / 1.17 MB to 20 files / 255 KB. + +## Local modifications + +Two deliberate divergences from upstream. Re-apply both when re-syncing. + +1. **`sixel.h` — `SIXELAPI` is empty.** Upstream defines it as + `__declspec(dllexport)` on Windows. This subset is compiled into `libfastfetch` + statically rather than into a standalone DLL, so the attribute would (a) add + every libsixel symbol to fastfetch's export table and (b) make `sixel.h`'s + declarations disagree with the ones in `dither.h` / `output.h` / `quant.h` / + `allocator.h`, which do not carry it (clang warns: + `-Wdll-attribute-on-redeclaration`). + +2. **`config.h` is hand-written.** Upstream generates it with `configure`. Only + these vendored files read it, and they reference a small fixed set of macros, + so the values are hard-coded. Two things to watch: + - `HAVE_TESTS` must stay **undefined**, not `0`: `sixel.h` gates its test-only + declarations with `#ifdef HAVE_TESTS`, so `#define HAVE_TESTS 0` would still + pull in declarations for functions we do not compile. + - `HAVE_MEMORY_H` / `HAVE_STRING_H` must be `1`. fastfetch builds with + `-Werror=implicit-function-declaration`, so a missing `` / + `` include in `pixelformat.c` / `status.c` is a hard error. + +The vendored sources are also exempted from `-Wconversion` in `CMakeLists.txt`: +upstream has ~160 implicit int→`unsigned char` narrowing warnings. Everything else +(`-Wall -Wextra` and fastfetch's `-Werror=` set) applies unchanged, and the subset +compiles clean. + +## Re-syncing with upstream + +```sh +VER=1.8.7-r2 +git clone --depth 1 --branch "$VER" https://github.com/saitoha/libsixel /tmp/libsixel + +# 1. copy the closure (paths change from src/foo.c to foo.c, include/sixel.h to sixel.h) +for f in tosixel.c dither.c dither.h output.c output.h quant.c quant.h \ + pixelformat.c allocator.c allocator.h status.c status.h malloc_stub.h; do + cp "/tmp/libsixel/src/$f" . +done +cp /tmp/libsixel/include/sixel.h . +cp /tmp/libsixel/LICENSE /tmp/libsixel/LICENSE.sixel /tmp/libsixel/LICENSE.pnmcolormap . + +# 2. re-apply the two local modifications listed above +# 3. verify +``` + +Verification (must be 0 diagnostics, and the encoder must emit a DCS-wrapped stream): + +```sh +CC=/c/msys64/clang64/bin/cc.exe +FLAGS="-I. -Wall -Wextra -Wconversion -Wno-conversion -Werror=uninitialized \ + -Werror=return-type -Werror=vla -Werror=incompatible-pointer-types \ + -Werror=implicit-function-declaration -Werror=int-conversion -std=gnu23" +for f in tosixel dither output quant allocator pixelformat status; do + $CC $FLAGS -c $f.c -o /dev/null || echo "FAILED: $f" +done +``` + +If upstream adds a call from the closure to a new file, the build fails at link +time with an undefined `sixel_*` symbol — add that file and repeat. + +## License + +MIT-compatible, but three separate notices apply and all three files must be kept. +Mapping verified against each source file's own copyright header: + +| File | Applies to | +|---|---| +| `LICENSE` | MIT, Copyright (c) 2014-2016 Hayaki Saito — `dither.c`, `output.c`, `pixelformat.c`, `allocator.c`, `status.c` (their headers say 2014-2018/2019; same MIT terms) | +| `LICENSE.sixel` | `tosixel.c` — derived from kmiya's original `sixel` (2014-3-2), permissive, re-licensed MIT by Hayaki Saito | +| `LICENSE.pnmcolormap` | `quant.c` — derived from `ppmquant` by Jef Poskanzer / Bryan Henderson | + +Upstream also ships `LICENSE.images`, `.mesa`, `.pngsuite`, `.sdump` and `.stb`; +none of them cover a file in this subset, so they were not carried over. If a +re-sync ever pulls in a file from the decoder side (`fromgif.c` → `.stb`, +`fromsixel.c` → `.sixel`), re-check this table. diff --git a/src/3rdparty/sixel/allocator.c b/src/3rdparty/sixel/allocator.c new file mode 100644 index 000000000..2264d83d4 --- /dev/null +++ b/src/3rdparty/sixel/allocator.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2014-2018 Hayaki Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +/* STDC_HEADERS */ +#include + +#if HAVE_ASSERT_H +# include +#endif /* HAVE_ASSERT_H */ +#if HAVE_SYS_TYPES_H +# include +#endif /* HAVE_SYS_TYPES_H */ +#if HAVE_ERRNO_H +# include +#endif /* HAVE_ERRNO_H */ +#if HAVE_MEMORY_H +# include +#endif /* HAVE_MEMORY_H */ + +#include "allocator.h" +#include "malloc_stub.h" + +/* create allocator object */ +SIXELSTATUS +sixel_allocator_new( + sixel_allocator_t /* out */ **ppallocator, /* allocator object to be created */ + sixel_malloc_t /* in */ fn_malloc, /* custom malloc() function */ + sixel_calloc_t /* in */ fn_calloc, /* custom calloc() function */ + sixel_realloc_t /* in */ fn_realloc, /* custom realloc() function */ + sixel_free_t /* in */ fn_free) /* custom free() function */ +{ + SIXELSTATUS status = SIXEL_FALSE; + + if (ppallocator == NULL) { + sixel_helper_set_additional_message( + "sixel_allocator_new: given argument ppallocator is null."); + status = SIXEL_BAD_ARGUMENT; + goto end; + } + + if (fn_malloc == NULL) { + fn_malloc = malloc; + } + + if (fn_calloc == NULL) { + fn_calloc = calloc; + } + + if (fn_realloc == NULL) { + fn_realloc = realloc; + } + + if (fn_free == NULL) { + fn_free = free; + } + + *ppallocator = fn_malloc(sizeof(sixel_allocator_t)); + if (*ppallocator == NULL) { + sixel_helper_set_additional_message( + "sixel_allocator_new: fn_malloc() failed."); + status = SIXEL_BAD_ALLOCATION; + goto end; + } + + (*ppallocator)->ref = 1; + (*ppallocator)->fn_malloc = fn_malloc; + (*ppallocator)->fn_calloc = fn_calloc; + (*ppallocator)->fn_realloc = fn_realloc; + (*ppallocator)->fn_free = fn_free; + + status = SIXEL_OK; + +end: + return status; +} + + +/* destruct allocator object */ +static void +sixel_allocator_destroy( + sixel_allocator_t /* in */ *allocator) /* allocator object to + be destroyed */ +{ + /* precondition */ + assert(allocator); + assert(allocator->fn_free); + + allocator->fn_free(allocator); +} + + +/* increase reference count of allocatort object (thread-unsafe) */ +SIXELAPI void +sixel_allocator_ref( + sixel_allocator_t /* in */ *allocator) /* allocator object to be + increment reference counter */ +{ + /* precondition */ + assert(allocator); + + /* TODO: be thread safe */ + ++allocator->ref; +} + + +/* decrease reference count of output context object (thread-unsafe) */ +SIXELAPI void +sixel_allocator_unref( + sixel_allocator_t /* in */ *allocator) /* allocator object to be unreference */ +{ + /* TODO: be thread safe */ + if (allocator) { + assert(allocator->ref > 0); + --allocator->ref; + if (allocator->ref == 0) { + sixel_allocator_destroy(allocator); + } + } +} + + +/* call custom malloc() */ +SIXELAPI void * +sixel_allocator_malloc( + sixel_allocator_t /* in */ *allocator, /* allocator object */ + size_t /* in */ n) /* allocation size */ +{ + /* precondition */ + assert(allocator); + assert(allocator->fn_malloc); + + if (n == 0) { + sixel_helper_set_additional_message( + "sixel_allocator_malloc: called with n == 0"); + return NULL; + } + + if (n > SIXEL_ALLOCATE_BYTES_MAX) { + return NULL; + } + + return allocator->fn_malloc(n); +} + + +/* call custom calloc() */ +SIXELAPI void * +sixel_allocator_calloc( + sixel_allocator_t /* in */ *allocator, /* allocator object */ + size_t /* in */ nelm, /* number of elements */ + size_t /* in */ elsize) /* size of element */ +{ + size_t n; + + /* precondition */ + assert(allocator); + assert(allocator->fn_calloc); + + n = nelm * elsize; + + if (n == 0) { + sixel_helper_set_additional_message( + "sixel_allocator_malloc: called with n == 0"); + return NULL; + } + + if (n > SIXEL_ALLOCATE_BYTES_MAX) { + return NULL; + } + + return allocator->fn_calloc(nelm, elsize); +} + + +/* call custom realloc() */ +SIXELAPI void * +sixel_allocator_realloc( + sixel_allocator_t /* in */ *allocator, /* allocator object */ + void /* in */ *p, /* existing buffer to be re-allocated */ + size_t /* in */ n) /* re-allocation size */ +{ + /* precondition */ + assert(allocator); + assert(allocator->fn_realloc); + + if (n == 0) { + sixel_helper_set_additional_message( + "sixel_allocator_malloc: called with n == 0"); + return NULL; + } + + if (n > SIXEL_ALLOCATE_BYTES_MAX) { + return NULL; + } + + return allocator->fn_realloc(p, n); +} + + +/* call custom free() */ +SIXELAPI void +sixel_allocator_free( + sixel_allocator_t /* in */ *allocator, /* allocator object */ + void /* in */ *p) /* existing buffer to be freed */ +{ + /* precondition */ + assert(allocator); + assert(allocator->fn_free); + + allocator->fn_free(p); +} + + +#if HAVE_TESTS +volatile int sixel_debug_malloc_counter; + +void * +sixel_bad_malloc(size_t size) +{ + return sixel_debug_malloc_counter-- == 0 ? NULL: malloc(size); +} + + +void * +sixel_bad_calloc(size_t count, size_t size) +{ + (void) count; + (void) size; + + return NULL; +} + + +void * +sixel_bad_realloc(void *ptr, size_t size) +{ + (void) ptr; + (void) size; + + return NULL; +} +#endif /* HAVE_TESTS */ + +#if 0 +int +rpl_posix_memalign(void **memptr, size_t alignment, size_t size) +{ +#if HAVE_POSIX_MEMALIGN + return posix_memalign(memptr, alignment, size); +#elif HAVE_ALIGNED_ALLOC + *memptr = aligned_alloc(alignment, size); + return *memptr ? 0: ENOMEM; +#elif HAVE_MEMALIGN + *memptr = memalign(alignment, size); + return *memptr ? 0: ENOMEM; +#elif HAVE__ALIGNED_MALLOC + return _aligned_malloc(size, alignment); +#else +# error +#endif /* _MSC_VER */ +} +#endif + + +#if HAVE_TESTS +static int +test1(void) +{ + int nret = EXIT_FAILURE; + SIXELSTATUS status; + sixel_allocator_t *allocator = NULL; + + status = sixel_allocator_new(NULL, malloc, calloc, realloc, free); + if (status != SIXEL_BAD_ARGUMENT) { + goto error; + } + + status = sixel_allocator_new(&allocator, NULL, calloc, realloc, free); + if (SIXEL_FAILED(status)) { + goto error; + } + + status = sixel_allocator_new(&allocator, malloc, NULL, realloc, free); + if (SIXEL_FAILED(status)) { + goto error; + } + + status = sixel_allocator_new(&allocator, malloc, calloc, NULL, free); + if (SIXEL_FAILED(status)) { + goto error; + } + + status = sixel_allocator_new(&allocator, malloc, calloc, realloc, NULL); + if (SIXEL_FAILED(status)) { + goto error; + } + + nret = EXIT_SUCCESS; + +error: + return nret; +} + + +static int +test2(void) +{ + int nret = EXIT_FAILURE; + SIXELSTATUS status; + sixel_allocator_t *allocator = NULL; + + sixel_debug_malloc_counter = 1; + + status = sixel_allocator_new(&allocator, sixel_bad_malloc, calloc, realloc, free); + if (status == SIXEL_BAD_ALLOCATION) { + goto error; + } + + nret = EXIT_SUCCESS; + +error: + return nret; +} + + +SIXELAPI int +sixel_allocator_tests_main(void) +{ + int nret = EXIT_FAILURE; + size_t i; + typedef int (* testcase)(void); + + static testcase const testcases[] = { + test1, + test2 + }; + + for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) { + nret = testcases[i](); + if (nret != EXIT_SUCCESS) { + goto error; + } + } + + nret = EXIT_SUCCESS; + +error: + return nret; +} +#endif /* HAVE_TESTS */ + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ diff --git a/src/3rdparty/sixel/allocator.h b/src/3rdparty/sixel/allocator.h new file mode 100644 index 000000000..c18da4b36 --- /dev/null +++ b/src/3rdparty/sixel/allocator.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014-2016 Hayaki Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef LIBSIXEL_ALLOCATOR_H +#define LIBSIXEL_ALLOCATOR_H + +#include + +struct sixel_allocator { + unsigned int ref; /* reference counter */ + sixel_malloc_t fn_malloc; + sixel_calloc_t fn_calloc; + sixel_realloc_t fn_realloc; + sixel_free_t fn_free; +}; + +#if HAVE_TESTS +int +sixel_allocator_tests_main(void); +#endif + +#endif /* LIBSIXEL_ALLOCATOR_H */ + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ diff --git a/src/3rdparty/sixel/config.h b/src/3rdparty/sixel/config.h new file mode 100644 index 000000000..4e34eb034 --- /dev/null +++ b/src/3rdparty/sixel/config.h @@ -0,0 +1,58 @@ +/* + * Minimal hand-written replacement for the autoconf-generated config.h. + * + * Upstream libsixel ships config.h.in and lets `configure` produce config.h. + * We only vendor the encoder-side subset (see README.md), and those files + * reference a small, fixed set of macros, so the values are hard-coded here + * instead of carrying the autotools machinery. + * + * The values below hold for every platform fastfetch targets: + * Windows/MinGW-w64, Linux/glibc, Linux/musl, macOS and the BSDs. + * Re-check this file whenever the vendored subset is re-synced with upstream. + */ + +#ifndef LIBSIXEL_CONFIG_H +#define LIBSIXEL_CONFIG_H + +/* Standard headers (upstream: AC_CHECK_HEADERS) */ +#define HAVE_ASSERT_H 1 +#define HAVE_ERRNO_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_LIMITS_H 1 +#define HAVE_MATH_H 1 +#define HAVE_MEMORY_H 1 +#define HAVE_STRING_H 1 +#define HAVE_SYS_TYPES_H 1 + +/* libc functions (upstream: AC_CHECK_FUNCS) */ +#define HAVE_LDIV 1 +#define HAVE_MALLOC 1 +#define HAVE_REALLOC 1 + +/* Byte order of the 32bpp pixel readers in pixelformat.c; 0 = little endian */ +#define SWAP_BYTES 0 + +/* + * Guards the diagnostic pragma push/pop in dither.c. Not needed, and the + * pragma spelling is not portable across the compilers we support. + */ +#define HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS 0 + +/* Verbose quantizer tracing to stderr */ +#define HAVE_DEBUG 0 + +/* + * HAVE_TESTS must stay *undefined* rather than 0: sixel.h gates its test-only + * declarations with `#ifdef HAVE_TESTS`, so defining it to 0 would still pull + * in declarations for functions we do not compile. + */ +#undef HAVE_TESTS + +/* Optional image loaders and features; not vendored */ +#undef HAVE_GD +#undef HAVE_GDK_PIXBUF2 +#undef HAVE_JPEG +#undef HAVE_LIBCURL +#undef HAVE_LIBPNG + +#endif /* LIBSIXEL_CONFIG_H */ diff --git a/src/3rdparty/sixel/dither.c b/src/3rdparty/sixel/dither.c new file mode 100644 index 000000000..e5353e8e1 --- /dev/null +++ b/src/3rdparty/sixel/dither.c @@ -0,0 +1,918 @@ +/* + * Copyright (c) 2014-2018 Hayaki Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include +#include + +#if HAVE_MATH_H +# include +#endif /* HAVE_MATH_H */ +#if HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#if HAVE_LIMITS_H +# include +#endif /* HAVE_LIMITS_H */ +#if HAVE_INTTYPES_H +# include +#endif /* HAVE_INTTYPES_H */ + +#include "dither.h" +#include "quant.h" +#include + + +static const unsigned char pal_mono_dark[] = { + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff +}; + + +static const unsigned char pal_mono_light[] = { + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00 +}; + +static const unsigned char pal_gray_1bit[] = { + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff +}; + + +static const unsigned char pal_gray_2bit[] = { + 0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xff +}; + + +static const unsigned char pal_gray_4bit[] = { + 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33, + 0x44, 0x44, 0x44, 0x55, 0x55, 0x55, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77, + 0x88, 0x88, 0x88, 0x99, 0x99, 0x99, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb, + 0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xee, 0xee, 0xee, 0xff, 0xff, 0xff +}; + + +static const unsigned char pal_gray_8bit[] = { + 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, + 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, + 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, + 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, + 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, + 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, + 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27, + 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, + 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, + 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, + 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x37, 0x37, 0x37, + 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, + 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, + 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, + 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x47, 0x47, 0x47, + 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, + 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, 0x4f, + 0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, + 0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, + 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, + 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f, + 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x63, + 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, + 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, + 0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, + 0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73, + 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, + 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b, + 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f, + 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, + 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87, + 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b, + 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f, + 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, + 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, + 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, + 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0x9f, + 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, + 0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa7, + 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab, 0xab, + 0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, + 0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3, + 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, + 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, + 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, + 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, + 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, + 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb, + 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, + 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, + 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, + 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, + 0xdc, 0xdc, 0xdc, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, + 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, + 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7, + 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, + 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, + 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, + 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, + 0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, + 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff +}; + + +static const unsigned char pal_xterm256[] = { + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00, + 0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0xc0, 0xc0, 0xc0, + 0x80, 0x80, 0x80, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x87, 0x00, 0x00, 0xaf, + 0x00, 0x00, 0xd7, 0x00, 0x00, 0xff, 0x00, 0x5f, 0x00, 0x00, 0x5f, 0x5f, + 0x00, 0x5f, 0x87, 0x00, 0x5f, 0xaf, 0x00, 0x5f, 0xd7, 0x00, 0x5f, 0xff, + 0x00, 0x87, 0x00, 0x00, 0x87, 0x5f, 0x00, 0x87, 0x87, 0x00, 0x87, 0xaf, + 0x00, 0x87, 0xd7, 0x00, 0x87, 0xff, 0x00, 0xaf, 0x00, 0x00, 0xaf, 0x5f, + 0x00, 0xaf, 0x87, 0x00, 0xaf, 0xaf, 0x00, 0xaf, 0xd7, 0x00, 0xaf, 0xff, + 0x00, 0xd7, 0x00, 0x00, 0xd7, 0x5f, 0x00, 0xd7, 0x87, 0x00, 0xd7, 0xaf, + 0x00, 0xd7, 0xd7, 0x00, 0xd7, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x5f, + 0x00, 0xff, 0x87, 0x00, 0xff, 0xaf, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff, + 0x5f, 0x00, 0x00, 0x5f, 0x00, 0x5f, 0x5f, 0x00, 0x87, 0x5f, 0x00, 0xaf, + 0x5f, 0x00, 0xd7, 0x5f, 0x00, 0xff, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x5f, + 0x5f, 0x5f, 0x87, 0x5f, 0x5f, 0xaf, 0x5f, 0x5f, 0xd7, 0x5f, 0x5f, 0xff, + 0x5f, 0x87, 0x00, 0x5f, 0x87, 0x5f, 0x5f, 0x87, 0x87, 0x5f, 0x87, 0xaf, + 0x5f, 0x87, 0xd7, 0x5f, 0x87, 0xff, 0x5f, 0xaf, 0x00, 0x5f, 0xaf, 0x5f, + 0x5f, 0xaf, 0x87, 0x5f, 0xaf, 0xaf, 0x5f, 0xaf, 0xd7, 0x5f, 0xaf, 0xff, + 0x5f, 0xd7, 0x00, 0x5f, 0xd7, 0x5f, 0x5f, 0xd7, 0x87, 0x5f, 0xd7, 0xaf, + 0x5f, 0xd7, 0xd7, 0x5f, 0xd7, 0xff, 0x5f, 0xff, 0x00, 0x5f, 0xff, 0x5f, + 0x5f, 0xff, 0x87, 0x5f, 0xff, 0xaf, 0x5f, 0xff, 0xd7, 0x5f, 0xff, 0xff, + 0x87, 0x00, 0x00, 0x87, 0x00, 0x5f, 0x87, 0x00, 0x87, 0x87, 0x00, 0xaf, + 0x87, 0x00, 0xd7, 0x87, 0x00, 0xff, 0x87, 0x5f, 0x00, 0x87, 0x5f, 0x5f, + 0x87, 0x5f, 0x87, 0x87, 0x5f, 0xaf, 0x87, 0x5f, 0xd7, 0x87, 0x5f, 0xff, + 0x87, 0x87, 0x00, 0x87, 0x87, 0x5f, 0x87, 0x87, 0x87, 0x87, 0x87, 0xaf, + 0x87, 0x87, 0xd7, 0x87, 0x87, 0xff, 0x87, 0xaf, 0x00, 0x87, 0xaf, 0x5f, + 0x87, 0xaf, 0x87, 0x87, 0xaf, 0xaf, 0x87, 0xaf, 0xd7, 0x87, 0xaf, 0xff, + 0x87, 0xd7, 0x00, 0x87, 0xd7, 0x5f, 0x87, 0xd7, 0x87, 0x87, 0xd7, 0xaf, + 0x87, 0xd7, 0xd7, 0x87, 0xd7, 0xff, 0x87, 0xff, 0x00, 0x87, 0xff, 0x5f, + 0x87, 0xff, 0x87, 0x87, 0xff, 0xaf, 0x87, 0xff, 0xd7, 0x87, 0xff, 0xff, + 0xaf, 0x00, 0x00, 0xaf, 0x00, 0x5f, 0xaf, 0x00, 0x87, 0xaf, 0x00, 0xaf, + 0xaf, 0x00, 0xd7, 0xaf, 0x00, 0xff, 0xaf, 0x5f, 0x00, 0xaf, 0x5f, 0x5f, + 0xaf, 0x5f, 0x87, 0xaf, 0x5f, 0xaf, 0xaf, 0x5f, 0xd7, 0xaf, 0x5f, 0xff, + 0xaf, 0x87, 0x00, 0xaf, 0x87, 0x5f, 0xaf, 0x87, 0x87, 0xaf, 0x87, 0xaf, + 0xaf, 0x87, 0xd7, 0xaf, 0x87, 0xff, 0xaf, 0xaf, 0x00, 0xaf, 0xaf, 0x5f, + 0xaf, 0xaf, 0x87, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xd7, 0xaf, 0xaf, 0xff, + 0xaf, 0xd7, 0x00, 0xaf, 0xd7, 0x5f, 0xaf, 0xd7, 0x87, 0xaf, 0xd7, 0xaf, + 0xaf, 0xd7, 0xd7, 0xaf, 0xd7, 0xff, 0xaf, 0xff, 0x00, 0xaf, 0xff, 0x5f, + 0xaf, 0xff, 0x87, 0xaf, 0xff, 0xaf, 0xaf, 0xff, 0xd7, 0xaf, 0xff, 0xff, + 0xd7, 0x00, 0x00, 0xd7, 0x00, 0x5f, 0xd7, 0x00, 0x87, 0xd7, 0x00, 0xaf, + 0xd7, 0x00, 0xd7, 0xd7, 0x00, 0xff, 0xd7, 0x5f, 0x00, 0xd7, 0x5f, 0x5f, + 0xd7, 0x5f, 0x87, 0xd7, 0x5f, 0xaf, 0xd7, 0x5f, 0xd7, 0xd7, 0x5f, 0xff, + 0xd7, 0x87, 0x00, 0xd7, 0x87, 0x5f, 0xd7, 0x87, 0x87, 0xd7, 0x87, 0xaf, + 0xd7, 0x87, 0xd7, 0xd7, 0x87, 0xff, 0xd7, 0xaf, 0x00, 0xd7, 0xaf, 0x5f, + 0xd7, 0xaf, 0x87, 0xd7, 0xaf, 0xaf, 0xd7, 0xaf, 0xd7, 0xd7, 0xaf, 0xff, + 0xd7, 0xd7, 0x00, 0xd7, 0xd7, 0x5f, 0xd7, 0xd7, 0x87, 0xd7, 0xd7, 0xaf, + 0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xff, 0xd7, 0xff, 0x00, 0xd7, 0xff, 0x5f, + 0xd7, 0xff, 0x87, 0xd7, 0xff, 0xaf, 0xd7, 0xff, 0xd7, 0xd7, 0xff, 0xff, + 0xff, 0x00, 0x00, 0xff, 0x00, 0x5f, 0xff, 0x00, 0x87, 0xff, 0x00, 0xaf, + 0xff, 0x00, 0xd7, 0xff, 0x00, 0xff, 0xff, 0x5f, 0x00, 0xff, 0x5f, 0x5f, + 0xff, 0x5f, 0x87, 0xff, 0x5f, 0xaf, 0xff, 0x5f, 0xd7, 0xff, 0x5f, 0xff, + 0xff, 0x87, 0x00, 0xff, 0x87, 0x5f, 0xff, 0x87, 0x87, 0xff, 0x87, 0xaf, + 0xff, 0x87, 0xd7, 0xff, 0x87, 0xff, 0xff, 0xaf, 0x00, 0xff, 0xaf, 0x5f, + 0xff, 0xaf, 0x87, 0xff, 0xaf, 0xaf, 0xff, 0xaf, 0xd7, 0xff, 0xaf, 0xff, + 0xff, 0xd7, 0x00, 0xff, 0xd7, 0x5f, 0xff, 0xd7, 0x87, 0xff, 0xd7, 0xaf, + 0xff, 0xd7, 0xd7, 0xff, 0xd7, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x5f, + 0xff, 0xff, 0x87, 0xff, 0xff, 0xaf, 0xff, 0xff, 0xd7, 0xff, 0xff, 0xff, + 0x08, 0x08, 0x08, 0x12, 0x12, 0x12, 0x1c, 0x1c, 0x1c, 0x26, 0x26, 0x26, + 0x30, 0x30, 0x30, 0x3a, 0x3a, 0x3a, 0x44, 0x44, 0x44, 0x4e, 0x4e, 0x4e, + 0x58, 0x58, 0x58, 0x62, 0x62, 0x62, 0x6c, 0x6c, 0x6c, 0x76, 0x76, 0x76, + 0x80, 0x80, 0x80, 0x8a, 0x8a, 0x8a, 0x94, 0x94, 0x94, 0x9e, 0x9e, 0x9e, + 0xa8, 0xa8, 0xa8, 0xb2, 0xb2, 0xb2, 0xbc, 0xbc, 0xbc, 0xc6, 0xc6, 0xc6, + 0xd0, 0xd0, 0xd0, 0xda, 0xda, 0xda, 0xe4, 0xe4, 0xe4, 0xee, 0xee, 0xee, +}; + + +/* + * VT340 undocumented behavior regarding the color palette reported + * by Vertis Sidus(@vrtsds): + * it loads the first fifteen colors as 1 through 15, and loads the + * sixteenth color as 0. + */ +static const unsigned char pal_vt340_mono[] = { + /* 1 Gray-2 */ 13 * 255 / 100, 13 * 255 / 100, 13 * 255 / 100, + /* 2 Gray-4 */ 26 * 255 / 100, 26 * 255 / 100, 26 * 255 / 100, + /* 3 Gray-6 */ 40 * 255 / 100, 40 * 255 / 100, 40 * 255 / 100, + /* 4 Gray-1 */ 6 * 255 / 100, 6 * 255 / 100, 6 * 255 / 100, + /* 5 Gray-3 */ 20 * 255 / 100, 20 * 255 / 100, 20 * 255 / 100, + /* 6 Gray-5 */ 33 * 255 / 100, 33 * 255 / 100, 33 * 255 / 100, + /* 7 White 7 */ 46 * 255 / 100, 46 * 255 / 100, 46 * 255 / 100, + /* 8 Black 0 */ 0 * 255 / 100, 0 * 255 / 100, 0 * 255 / 100, + /* 9 Gray-2 */ 13 * 255 / 100, 13 * 255 / 100, 13 * 255 / 100, + /* 10 Gray-4 */ 26 * 255 / 100, 26 * 255 / 100, 26 * 255 / 100, + /* 11 Gray-6 */ 40 * 255 / 100, 40 * 255 / 100, 40 * 255 / 100, + /* 12 Gray-1 */ 6 * 255 / 100, 6 * 255 / 100, 6 * 255 / 100, + /* 13 Gray-3 */ 20 * 255 / 100, 20 * 255 / 100, 20 * 255 / 100, + /* 14 Gray-5 */ 33 * 255 / 100, 33 * 255 / 100, 33 * 255 / 100, + /* 15 White 7 */ 46 * 255 / 100, 46 * 255 / 100, 46 * 255 / 100, + /* 0 Black */ 0 * 255 / 100, 0 * 255 / 100, 0 * 255 / 100, +}; + + +static const unsigned char pal_vt340_color[] = { + /* 1 Blue */ 20 * 255 / 100, 20 * 255 / 100, 80 * 255 / 100, + /* 2 Red */ 80 * 255 / 100, 13 * 255 / 100, 13 * 255 / 100, + /* 3 Green */ 20 * 255 / 100, 80 * 255 / 100, 20 * 255 / 100, + /* 4 Magenta */ 80 * 255 / 100, 20 * 255 / 100, 80 * 255 / 100, + /* 5 Cyan */ 20 * 255 / 100, 80 * 255 / 100, 80 * 255 / 100, + /* 6 Yellow */ 80 * 255 / 100, 80 * 255 / 100, 20 * 255 / 100, + /* 7 Gray 50% */ 53 * 255 / 100, 53 * 255 / 100, 53 * 255 / 100, + /* 8 Gray 25% */ 26 * 255 / 100, 26 * 255 / 100, 26 * 255 / 100, + /* 9 Blue* */ 33 * 255 / 100, 33 * 255 / 100, 60 * 255 / 100, + /* 10 Red* */ 60 * 255 / 100, 26 * 255 / 100, 26 * 255 / 100, + /* 11 Green* */ 33 * 255 / 100, 60 * 255 / 100, 33 * 255 / 100, + /* 12 Magenta* */ 60 * 255 / 100, 33 * 255 / 100, 60 * 255 / 100, + /* 13 Cyan* */ 33 * 255 / 100, 60 * 255 / 100, 60 * 255 / 100, + /* 14 Yellow* */ 60 * 255 / 100, 60 * 255 / 100, 33 * 255 / 100, + /* 15 Gray 75% */ 80 * 255 / 100, 80 * 255 / 100, 80 * 255 / 100, + /* 0 Black */ 0 * 255 / 100, 0 * 255 / 100, 0 * 255 / 100, +}; + + +/* create dither context object */ +SIXELAPI SIXELSTATUS +sixel_dither_new( + sixel_dither_t /* out */ **ppdither, /* dither object to be created */ + int /* in */ ncolors, /* required colors */ + sixel_allocator_t /* in */ *allocator) /* allocator, null if you use + default allocator */ +{ + SIXELSTATUS status = SIXEL_FALSE; + size_t headsize; + size_t datasize; + size_t wholesize; + int quality_mode; + + /* ensure given pointer is not null */ + if (ppdither == NULL) { + sixel_helper_set_additional_message( + "sixel_dither_new: ppdither is null."); + status = SIXEL_BAD_ARGUMENT; + goto end; + } + + if (allocator == NULL) { + status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL); + if (SIXEL_FAILED(status)) { + *ppdither = NULL; + goto end; + } + } else { + sixel_allocator_ref(allocator); + } + + if (ncolors < 0) { + ncolors = SIXEL_PALETTE_MAX; + quality_mode = SIXEL_QUALITY_HIGHCOLOR; + } else { + if (ncolors > SIXEL_PALETTE_MAX) { + status = SIXEL_BAD_INPUT; + goto end; + } else if (ncolors < 1) { + status = SIXEL_BAD_INPUT; + sixel_helper_set_additional_message( + "sixel_dither_new: palette colors must be more than 0"); + goto end; + } + quality_mode = SIXEL_QUALITY_LOW; + } + headsize = sizeof(sixel_dither_t); + datasize = (size_t)(ncolors * 3); + wholesize = headsize + datasize; + + *ppdither = (sixel_dither_t *)sixel_allocator_malloc(allocator, wholesize); + if (*ppdither == NULL) { + sixel_allocator_unref(allocator); + sixel_helper_set_additional_message( + "sixel_dither_new: sixel_allocator_malloc() failed."); + status = SIXEL_BAD_ALLOCATION; + goto end; + } + + (*ppdither)->ref = 1; + (*ppdither)->palette = (unsigned char*)(*ppdither + 1); + (*ppdither)->cachetable = NULL; + (*ppdither)->reqcolors = ncolors; + (*ppdither)->ncolors = ncolors; + (*ppdither)->origcolors = (-1); + (*ppdither)->keycolor = (-1); + (*ppdither)->optimized = 0; + (*ppdither)->optimize_palette = 0; + (*ppdither)->complexion = 1; + (*ppdither)->bodyonly = 0; + (*ppdither)->method_for_largest = SIXEL_LARGE_NORM; + (*ppdither)->method_for_rep = SIXEL_REP_CENTER_BOX; + (*ppdither)->method_for_diffuse = SIXEL_DIFFUSE_FS; + (*ppdither)->quality_mode = quality_mode; + (*ppdither)->pixelformat = SIXEL_PIXELFORMAT_RGB888; + (*ppdither)->allocator = allocator; + + status = SIXEL_OK; + +end: + return status; +} + + +/* create dither context object (deprecated) */ +SIXELAPI sixel_dither_t * +sixel_dither_create( + int /* in */ ncolors) +{ + SIXELSTATUS status = SIXEL_FALSE; + sixel_dither_t *dither = NULL; + + status = sixel_dither_new(&dither, ncolors, NULL); + if (SIXEL_FAILED(status)) { + goto end; + } + +end: + return dither; +} + + +SIXELAPI void +sixel_dither_destroy( + sixel_dither_t /* in */ *dither) +{ + sixel_allocator_t *allocator; + + if (dither) { + allocator = dither->allocator; + sixel_allocator_free(allocator, dither->cachetable); + dither->cachetable = NULL; + sixel_allocator_free(allocator, dither); + sixel_allocator_unref(allocator); + } +} + + +SIXELAPI void +sixel_dither_ref( + sixel_dither_t /* in */ *dither) +{ + /* TODO: be thread safe */ + ++dither->ref; +} + + +SIXELAPI void +sixel_dither_unref( + sixel_dither_t /* in */ *dither) +{ + /* TODO: be thread safe */ + if (dither != NULL && --dither->ref == 0) { + sixel_dither_destroy(dither); + } +} + + +SIXELAPI sixel_dither_t * +sixel_dither_get( + int /* in */ builtin_dither) +{ + SIXELSTATUS status = SIXEL_FALSE; + unsigned char *palette; + int ncolors; + int keycolor; + sixel_dither_t *dither = NULL; + + switch (builtin_dither) { + case SIXEL_BUILTIN_MONO_DARK: + ncolors = 2; + palette = (unsigned char *)pal_mono_dark; + keycolor = 0; + break; + case SIXEL_BUILTIN_MONO_LIGHT: + ncolors = 2; + palette = (unsigned char *)pal_mono_light; + keycolor = 0; + break; + case SIXEL_BUILTIN_XTERM16: + ncolors = 16; + palette = (unsigned char *)pal_xterm256; + keycolor = (-1); + break; + case SIXEL_BUILTIN_XTERM256: + ncolors = 256; + palette = (unsigned char *)pal_xterm256; + keycolor = (-1); + break; + case SIXEL_BUILTIN_VT340_MONO: + ncolors = 16; + palette = (unsigned char *)pal_vt340_mono; + keycolor = (-1); + break; + case SIXEL_BUILTIN_VT340_COLOR: + ncolors = 16; + palette = (unsigned char *)pal_vt340_color; + keycolor = (-1); + break; + case SIXEL_BUILTIN_G1: + ncolors = 2; + palette = (unsigned char *)pal_gray_1bit; + keycolor = (-1); + break; + case SIXEL_BUILTIN_G2: + ncolors = 4; + palette = (unsigned char *)pal_gray_2bit; + keycolor = (-1); + break; + case SIXEL_BUILTIN_G4: + ncolors = 16; + palette = (unsigned char *)pal_gray_4bit; + keycolor = (-1); + break; + case SIXEL_BUILTIN_G8: + ncolors = 256; + palette = (unsigned char *)pal_gray_8bit; + keycolor = (-1); + break; + default: + goto end; + } + + status = sixel_dither_new(&dither, ncolors, NULL); + if (SIXEL_FAILED(status)) { + dither = NULL; + goto end; + } + + dither->palette = palette; + dither->keycolor = keycolor; + dither->optimized = 1; + dither->optimize_palette = 0; + +end: + return dither; +} + + +static void +sixel_dither_set_method_for_largest( + sixel_dither_t /* in */ *dither, + int /* in */ method_for_largest) +{ + if (method_for_largest == SIXEL_LARGE_AUTO) { + method_for_largest = SIXEL_LARGE_NORM; + } + dither->method_for_largest = method_for_largest; +} + + +static void +sixel_dither_set_method_for_rep( + sixel_dither_t /* in */ *dither, + int /* in */ method_for_rep) +{ + if (method_for_rep == SIXEL_REP_AUTO) { + method_for_rep = SIXEL_REP_CENTER_BOX; + } + dither->method_for_rep = method_for_rep; +} + + +static void +sixel_dither_set_quality_mode( + sixel_dither_t /* in */ *dither, + int /* in */ quality_mode) +{ + if (quality_mode == SIXEL_QUALITY_AUTO) { + if (dither->ncolors <= 8) { + quality_mode = SIXEL_QUALITY_HIGH; + } else { + quality_mode = SIXEL_QUALITY_LOW; + } + } + dither->quality_mode = quality_mode; +} + + +SIXELAPI SIXELSTATUS +sixel_dither_initialize( + sixel_dither_t /* in */ *dither, + unsigned char /* in */ *data, + int /* in */ width, + int /* in */ height, + int /* in */ pixelformat, + int /* in */ method_for_largest, + int /* in */ method_for_rep, + int /* in */ quality_mode) +{ + unsigned char *buf = NULL; + unsigned char *normalized_pixels = NULL; + unsigned char *input_pixels; + SIXELSTATUS status = SIXEL_FALSE; + + /* ensure dither object is not null */ + if (dither == NULL) { + sixel_helper_set_additional_message( + "sixel_dither_new: dither is null."); + status = SIXEL_BAD_ARGUMENT; + goto end; + } + + /* increment ref count */ + sixel_dither_ref(dither); + + sixel_dither_set_pixelformat(dither, pixelformat); + + switch (pixelformat) { + case SIXEL_PIXELFORMAT_RGB888: + input_pixels = data; + break; + default: + /* normalize pixelformat */ + normalized_pixels + = (unsigned char *)sixel_allocator_malloc(dither->allocator, (size_t)(width * height * 3)); + if (normalized_pixels == NULL) { + sixel_helper_set_additional_message( + "sixel_dither_initialize: sixel_allocator_malloc() failed."); + status = SIXEL_BAD_ALLOCATION; + goto end; + } + + status = sixel_helper_normalize_pixelformat( + normalized_pixels, + &pixelformat, + data, + pixelformat, + width, + height); + if (SIXEL_FAILED(status)) { + goto end; + } + input_pixels = normalized_pixels; + break; + } + + sixel_dither_set_method_for_largest(dither, method_for_largest); + sixel_dither_set_method_for_rep(dither, method_for_rep); + sixel_dither_set_quality_mode(dither, quality_mode); + + status = sixel_quant_make_palette(&buf, + input_pixels, + (unsigned int)(width * height * 3), + SIXEL_PIXELFORMAT_RGB888, + (unsigned int)dither->reqcolors, + (unsigned int *)&dither->ncolors, + (unsigned int *)&dither->origcolors, + dither->method_for_largest, + dither->method_for_rep, + dither->quality_mode, + dither->allocator); + if (SIXEL_FAILED(status)) { + goto end; + } + memcpy(dither->palette, buf, (size_t)(dither->ncolors * 3)); + + dither->optimized = 1; + if (dither->origcolors <= dither->ncolors) { + dither->method_for_diffuse = SIXEL_DIFFUSE_NONE; + } + + sixel_quant_free_palette(buf, dither->allocator); + status = SIXEL_OK; + +end: + free(normalized_pixels); + + /* decrement ref count */ + sixel_dither_unref(dither); + + return status; +} + + +/* set diffusion type, choose from enum methodForDiffuse */ +SIXELAPI void +sixel_dither_set_diffusion_type( + sixel_dither_t /* in */ *dither, + int /* in */ method_for_diffuse) +{ + if (method_for_diffuse == SIXEL_DIFFUSE_AUTO) { + if (dither->ncolors > 16) { + method_for_diffuse = SIXEL_DIFFUSE_FS; + } else { + method_for_diffuse = SIXEL_DIFFUSE_ATKINSON; + } + } + dither->method_for_diffuse = method_for_diffuse; +} + + +/* get number of palette colors */ +SIXELAPI int +sixel_dither_get_num_of_palette_colors( + sixel_dither_t /* in */ *dither) +{ + return dither->ncolors; +} + + +/* get number of histogram colors */ +SIXELAPI int +sixel_dither_get_num_of_histogram_colors( + sixel_dither_t /* in */ *dither) /* dither context object */ +{ + return dither->origcolors; +} + + +/* typoed: remained for keeping compatibility */ +SIXELAPI int +sixel_dither_get_num_of_histgram_colors( + sixel_dither_t /* in */ *dither) /* dither context object */ +{ + return sixel_dither_get_num_of_histogram_colors(dither); +} + + +/* get palette */ +SIXELAPI unsigned char * +sixel_dither_get_palette( + sixel_dither_t /* in */ *dither) /* dither context object */ +{ + return dither->palette; +} + + +/* set palette */ +SIXELAPI void +sixel_dither_set_palette( + sixel_dither_t /* in */ *dither, /* dither context object */ + unsigned char /* in */ *palette) +{ + memcpy(dither->palette, palette, (size_t)(dither->ncolors * 3)); +} + + +/* set the factor of complexion color correcting */ +SIXELAPI void +sixel_dither_set_complexion_score( + sixel_dither_t /* in */ *dither, /* dither context object */ + int /* in */ score) /* complexion score (>= 1) */ +{ + dither->complexion = score; +} + + +/* set whether omitting palette difinition */ +SIXELAPI void +sixel_dither_set_body_only( + sixel_dither_t /* in */ *dither, /* dither context object */ + int /* in */ bodyonly) /* 0: output palette section + 1: do not output palette section */ +{ + dither->bodyonly = bodyonly; +} + + +/* set whether optimize palette size */ +SIXELAPI void +sixel_dither_set_optimize_palette( + sixel_dither_t /* in */ *dither, /* dither context object */ + int /* in */ do_opt) /* 0: optimize palette size + 1: don't optimize palette size */ +{ + dither->optimize_palette = do_opt; +} + + +/* set pixelformat */ +SIXELAPI void +sixel_dither_set_pixelformat( + sixel_dither_t /* in */ *dither, /* dither context object */ + int /* in */ pixelformat) /* one of enum pixelFormat */ +{ + dither->pixelformat = pixelformat; +} + + +/* set transparent */ +SIXELAPI void +sixel_dither_set_transparent( + sixel_dither_t /* in */ *dither, /* dither context object */ + int /* in */ transparent) /* transparent color index */ +{ + dither->keycolor = transparent; +} + + +/* set transparent */ +SIXELAPI sixel_index_t * +sixel_dither_apply_palette( + sixel_dither_t /* in */ *dither, + unsigned char /* in */ *pixels, + int /* in */ width, + int /* in */ height) +{ + SIXELSTATUS status = SIXEL_FALSE; + size_t bufsize; + sixel_index_t *dest = NULL; + int ncolors; + unsigned char *normalized_pixels = NULL; + unsigned char *input_pixels; + + /* ensure dither object is not null */ + if (dither == NULL) { + sixel_helper_set_additional_message( + "sixel_dither_apply_palette: dither is null."); + status = SIXEL_BAD_ARGUMENT; + goto end; + } + + sixel_dither_ref(dither); + + bufsize = (size_t)(width * height) * sizeof(sixel_index_t); + dest = (sixel_index_t *)sixel_allocator_malloc(dither->allocator, bufsize); + if (dest == NULL) { + sixel_helper_set_additional_message( + "sixel_dither_new: sixel_allocator_malloc() failed."); + status = SIXEL_BAD_ALLOCATION; + goto end; + } + + /* if quality_mode is full, do not use palette caching */ + if (dither->quality_mode == SIXEL_QUALITY_FULL) { + dither->optimized = 0; + } + + if (dither->cachetable == NULL && dither->optimized) { + if (dither->palette != pal_mono_dark && dither->palette != pal_mono_light) { + dither->cachetable = (unsigned short *)sixel_allocator_calloc(dither->allocator, + (size_t)(1 << 3 * 5), + sizeof(unsigned short)); + if (dither->cachetable == NULL) { + sixel_helper_set_additional_message( + "sixel_dither_new: sixel_allocator_calloc() failed."); + status = SIXEL_BAD_ALLOCATION; + goto end; + } + } + } + + if (dither->pixelformat != SIXEL_PIXELFORMAT_RGB888) { + /* normalize pixelformat */ + normalized_pixels + = (unsigned char *)sixel_allocator_malloc(dither->allocator, (size_t)(width * height * 3)); + if (normalized_pixels == NULL) { + sixel_helper_set_additional_message( + "sixel_dither_new: sixel_allocator_malloc() failed."); + status = SIXEL_BAD_ALLOCATION; + goto end; + } + status = sixel_helper_normalize_pixelformat(normalized_pixels, + &dither->pixelformat, + pixels, dither->pixelformat, + width, height); + if (SIXEL_FAILED(status)) { + goto end; + } + input_pixels = normalized_pixels; + } else { + input_pixels = pixels; + } + + status = sixel_quant_apply_palette(dest, + input_pixels, + width, height, 3, + dither->palette, + dither->ncolors, + dither->method_for_diffuse, + dither->optimized, + dither->optimize_palette, + dither->complexion, + dither->cachetable, + &ncolors, + dither->allocator); + if (SIXEL_FAILED(status)) { + free(dest); + dest = NULL; + goto end; + } + + dither->ncolors = ncolors; + +end: + free(normalized_pixels); + sixel_dither_unref(dither); + return dest; +} + + +#if HAVE_TESTS +static int +test1(void) +{ + sixel_dither_t *dither = NULL; + int nret = EXIT_FAILURE; + +#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + dither = sixel_dither_create(2); +#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS +# pragma GCC diagnostic pop +#endif + if (dither == NULL) { + goto error; + } + sixel_dither_ref(dither); + sixel_dither_unref(dither); + nret = EXIT_SUCCESS; + +error: + sixel_dither_unref(dither); + return nret; +} + +static int +test2(void) +{ + sixel_dither_t *dither = NULL; + int colors; + int nret = EXIT_FAILURE; + +#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + dither = sixel_dither_create(INT_MAX); +#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS +# pragma GCC diagnostic pop +#endif + if (dither == NULL) { + goto error; + } + sixel_dither_set_body_only(dither, 1); + colors = sixel_dither_get_num_of_histogram_colors(dither); + if (colors != -1) { + goto error; + } + nret = EXIT_SUCCESS; + +error: + sixel_dither_unref(dither); + return nret; +} + + +SIXELAPI int +sixel_dither_tests_main(void) +{ + int nret = EXIT_FAILURE; + size_t i; + typedef int (* testcase)(void); + + static testcase const testcases[] = { + test1, + test2, + }; + + for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) { + nret = testcases[i](); + if (nret != EXIT_SUCCESS) { + goto error; + } + } + + nret = EXIT_SUCCESS; + +error: + return nret; +} +#endif /* HAVE_TESTS */ + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ diff --git a/src/3rdparty/sixel/dither.h b/src/3rdparty/sixel/dither.h new file mode 100644 index 000000000..1c6d203a6 --- /dev/null +++ b/src/3rdparty/sixel/dither.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014-2016 Hayaki Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef LIBSIXEL_DITHER_H +#define LIBSIXEL_DITHER_H + +#include + +/* dither context object */ +struct sixel_dither { + unsigned int ref; /* reference counter */ + unsigned char *palette; /* palette definition */ + unsigned short *cachetable; /* cache table */ + int reqcolors; /* requested colors */ + int ncolors; /* active colors */ + int origcolors; /* original colors */ + int optimized; /* pixel is 15bpp compressable */ + int optimize_palette; /* minimize palette size */ + int complexion; /* for complexion correction */ + int bodyonly; /* do not output palette section if true */ + int method_for_largest; /* method for finding the largest dimention + for splitting */ + int method_for_rep; /* method for choosing a color from the box */ + int method_for_diffuse; /* method for diffusing */ + int quality_mode; /* quality of histogram */ + int keycolor; /* background color */ + int pixelformat; /* pixelformat for internal processing */ + sixel_allocator_t *allocator; /* allocator */ +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/* apply palette */ +sixel_index_t * +sixel_dither_apply_palette(struct sixel_dither /* in */ *dither, + unsigned char /* in */ *pixels, + int /* in */ width, + int /* in */ height); + +#if HAVE_TESTS +int +sixel_frame_tests_main(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LIBSIXEL_DITHER_H */ + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ diff --git a/src/3rdparty/sixel/malloc_stub.h b/src/3rdparty/sixel/malloc_stub.h new file mode 100644 index 000000000..2f2621e89 --- /dev/null +++ b/src/3rdparty/sixel/malloc_stub.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014-2016 Hayaki Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MALLOC_STUB_H +#define MALLOC_STUB_H + +#if HAVE_SYS_TYPES_H +#include +#endif /* HAVE_SYS_TYPES_H */ + +#if !HAVE_MALLOC +void * rpl_malloc(size_t n); +#endif /* !HAVE_MALLOC */ + +#if !HAVE_REALLOC +void * rpl_realloc(void *p, size_t n); +#endif /* !HAVE_REALLOC */ + +#if 0 +int rpl_posix_memalign(void **memptr, size_t alignment, size_t size); +#endif + +#endif /* MALLOC_STUB_H */ + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ diff --git a/src/3rdparty/sixel/output.c b/src/3rdparty/sixel/output.c new file mode 100644 index 000000000..9efd97d78 --- /dev/null +++ b/src/3rdparty/sixel/output.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2014-2019 Hayaki Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +/* STDC_HEADERS */ +# include +# include + +#if HAVE_ASSERT_H +# include +#endif /* HAVE_ASSERT_H */ + +#include +#include "output.h" + + +/* create new output context object */ +SIXELAPI SIXELSTATUS +sixel_output_new( + sixel_output_t /* out */ **output, + sixel_write_function /* in */ fn_write, + void /* in */ *priv, + sixel_allocator_t /* in */ *allocator) +{ + SIXELSTATUS status = SIXEL_FALSE; + size_t size; + + if (allocator == NULL) { + status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL); + if (SIXEL_FAILED(status)) { + goto end; + } + } else { + sixel_allocator_ref(allocator); + } + size = sizeof(sixel_output_t) + SIXEL_OUTPUT_PACKET_SIZE * 2; + + *output = (sixel_output_t *)sixel_allocator_malloc(allocator, size); + if (*output == NULL) { + sixel_helper_set_additional_message( + "sixel_output_new: sixel_allocator_malloc() failed."); + status = SIXEL_BAD_ALLOCATION; + goto end; + } + + (*output)->ref = 1; + (*output)->has_8bit_control = 0; + (*output)->has_sdm_glitch = 0; + (*output)->has_gri_arg_limit = 1; + (*output)->skip_dcs_envelope = 0; + (*output)->palette_type = SIXEL_PALETTETYPE_AUTO; + (*output)->fn_write = fn_write; + (*output)->save_pixel = 0; + (*output)->save_count = 0; + (*output)->active_palette = (-1); + (*output)->node_top = NULL; + (*output)->node_free = NULL; + (*output)->priv = priv; + (*output)->pos = 0; + (*output)->penetrate_multiplexer = 0; + (*output)->encode_policy = SIXEL_ENCODEPOLICY_AUTO; + (*output)->allocator = allocator; + + status = SIXEL_OK; + +end: + return status; +} + + +/* deprecated: create an output object */ +SIXELAPI sixel_output_t * +sixel_output_create(sixel_write_function fn_write, void *priv) +{ + SIXELSTATUS status = SIXEL_FALSE; + sixel_output_t *output = NULL; + + status = sixel_output_new(&output, fn_write, priv, NULL); + if (SIXEL_FAILED(status)) { + goto end; + } + +end: + return output; +} + + +/* destroy output context object */ +SIXELAPI void +sixel_output_destroy(sixel_output_t *output) +{ + sixel_allocator_t *allocator; + + if (output) { + allocator = output->allocator; + sixel_allocator_free(allocator, output); + sixel_allocator_unref(allocator); + } +} + + +/* increase reference count of output context object (thread-unsafe) */ +SIXELAPI void +sixel_output_ref(sixel_output_t *output) +{ + /* TODO: be thread-safe */ + ++output->ref; +} + + +/* decrease reference count of output context object (thread-unsafe) */ +SIXELAPI void +sixel_output_unref(sixel_output_t *output) +{ + /* TODO: be thread-safe */ + if (output) { + assert(output->ref > 0); + output->ref--; + if (output->ref == 0) { + sixel_output_destroy(output); + } + } +} + + +/* get 8bit output mode which indicates whether it uses C1 control characters */ +SIXELAPI int +sixel_output_get_8bit_availability(sixel_output_t *output) +{ + return output->has_8bit_control; +} + + +/* set 8bit output mode state */ +SIXELAPI void +sixel_output_set_8bit_availability(sixel_output_t *output, int availability) +{ + output->has_8bit_control = availability; +} + + +/* set whether limit arguments of DECGRI('!') to 255 */ +SIXELAPI void +sixel_output_set_gri_arg_limit( + sixel_output_t /* in */ *output, /* output context */ + int /* in */ value) /* 0: don't limit arguments of DECGRI + 1: limit arguments of DECGRI to 255 */ +{ + output->has_gri_arg_limit = value; +} + + +/* set GNU Screen penetration feature enable or disable */ +SIXELAPI void +sixel_output_set_penetrate_multiplexer(sixel_output_t *output, int penetrate) +{ + output->penetrate_multiplexer = penetrate; +} + + +/* set whether we skip DCS envelope */ +SIXELAPI void +sixel_output_set_skip_dcs_envelope(sixel_output_t *output, int skip) +{ + output->skip_dcs_envelope = skip; +} + + +/* set palette type: RGB or HLS */ +SIXELAPI void +sixel_output_set_palette_type(sixel_output_t *output, int palettetype) +{ + output->palette_type = palettetype; +} + + +/* set encodeing policy: auto, fast or size */ +SIXELAPI void +sixel_output_set_encode_policy(sixel_output_t *output, int encode_policy) +{ + output->encode_policy = encode_policy; +} + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ diff --git a/src/3rdparty/sixel/output.h b/src/3rdparty/sixel/output.h new file mode 100644 index 000000000..197d09781 --- /dev/null +++ b/src/3rdparty/sixel/output.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014-2016 Hayaki Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef LIBSIXEL_OUTPUT_H +#define LIBSIXEL_OUTPUT_H + +typedef struct sixel_node { + struct sixel_node *next; + int pal; + int sx; + int mx; + char *map; +} sixel_node_t; + +struct sixel_output { + + int ref; + sixel_allocator_t *allocator; + + /* compatiblity flags */ + + /* 0: 7bit terminal, + * 1: 8bit terminal */ + unsigned char has_8bit_control; + + /* 0: the terminal has sixel scrolling + * 1: the terminal does not have sixel scrolling */ + unsigned char has_sixel_scrolling; + + /* 1: the argument of repeat introducer(DECGRI) is not limitted + 0: the argument of repeat introducer(DECGRI) is limitted 255 */ + unsigned char has_gri_arg_limit; + + /* 0: DECSDM set (CSI ? 80 h) enables sixel scrolling + 1: DECSDM set (CSI ? 80 h) disables sixel scrolling */ + unsigned char has_sdm_glitch; + + /* 0: do not skip DCS envelope + * 1: skip DCS envelope */ + unsigned char skip_dcs_envelope; + + /* PALETTETYPE_AUTO: select palette type automatically + * PALETTETYPE_HLS : HLS color space + * PALETTETYPE_RGB : RGB color space */ + unsigned char palette_type; + + sixel_write_function fn_write; + + int save_pixel; + int save_count; + int active_palette; + + sixel_node_t *node_top; + sixel_node_t *node_free; + + int penetrate_multiplexer; + int encode_policy; + + void *priv; + int pos; + unsigned char buffer[1]; +}; + +#endif /* LIBSIXEL_OUTPUT_H */ + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ diff --git a/src/3rdparty/sixel/pixelformat.c b/src/3rdparty/sixel/pixelformat.c new file mode 100644 index 000000000..60b22f919 --- /dev/null +++ b/src/3rdparty/sixel/pixelformat.c @@ -0,0 +1,725 @@ +/* + * Copyright (c) 2014-2019 Hayaki Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +/* STDC_HEADERS */ +#include +#include + +#if HAVE_MEMORY_H +# include +#endif /* HAVE_MEMORY_H */ + +#include + +static void +get_rgb(unsigned char const *data, + int const pixelformat, + int depth, + unsigned char *r, + unsigned char *g, + unsigned char *b) +{ + unsigned int pixels = 0; +#if SWAP_BYTES + unsigned int low; + unsigned int high; +#endif + int count = 0; + + while (count < depth) { + pixels = *(data + count) | (pixels << 8); + count++; + } + + /* TODO: we should swap bytes (only necessary on LSByte first hardware?) */ +#if SWAP_BYTES + if (depth == 2) { + low = pixels & 0xff; + high = (pixels >> 8) & 0xff; + pixels = (low << 8) | high; + } +#endif + + switch (pixelformat) { + case SIXEL_PIXELFORMAT_RGB555: + *r = ((pixels >> 10) & 0x1f) << 3; + *g = ((pixels >> 5) & 0x1f) << 3; + *b = ((pixels >> 0) & 0x1f) << 3; + break; + case SIXEL_PIXELFORMAT_RGB565: + *r = ((pixels >> 11) & 0x1f) << 3; + *g = ((pixels >> 5) & 0x3f) << 2; + *b = ((pixels >> 0) & 0x1f) << 3; + break; + case SIXEL_PIXELFORMAT_RGB888: + *r = (pixels >> 16) & 0xff; + *g = (pixels >> 8) & 0xff; + *b = (pixels >> 0) & 0xff; + break; + case SIXEL_PIXELFORMAT_BGR555: + *r = ((pixels >> 0) & 0x1f) << 3; + *g = ((pixels >> 5) & 0x1f) << 3; + *b = ((pixels >> 10) & 0x1f) << 3; + break; + case SIXEL_PIXELFORMAT_BGR565: + *r = ((pixels >> 0) & 0x1f) << 3; + *g = ((pixels >> 5) & 0x3f) << 2; + *b = ((pixels >> 11) & 0x1f) << 3; + break; + case SIXEL_PIXELFORMAT_BGR888: + *r = (pixels >> 0) & 0xff; + *g = (pixels >> 8) & 0xff; + *b = (pixels >> 16) & 0xff; + break; + case SIXEL_PIXELFORMAT_RGBA8888: + *r = (pixels >> 24) & 0xff; + *g = (pixels >> 16) & 0xff; + *b = (pixels >> 8) & 0xff; + break; + case SIXEL_PIXELFORMAT_ARGB8888: + *r = (pixels >> 16) & 0xff; + *g = (pixels >> 8) & 0xff; + *b = (pixels >> 0) & 0xff; + break; + case SIXEL_PIXELFORMAT_BGRA8888: + *r = (pixels >> 8) & 0xff; + *g = (pixels >> 16) & 0xff; + *b = (pixels >> 24) & 0xff; + break; + case SIXEL_PIXELFORMAT_ABGR8888: + *r = (pixels >> 0) & 0xff; + *g = (pixels >> 8) & 0xff; + *b = (pixels >> 16) & 0xff; + break; + case SIXEL_PIXELFORMAT_GA88: + *r = *g = *b = (pixels >> 8) & 0xff; + break; + case SIXEL_PIXELFORMAT_G8: + case SIXEL_PIXELFORMAT_AG88: + *r = *g = *b = pixels & 0xff; + break; + default: + *r = *g = *b = 0; + break; + } +} + + +SIXELAPI int +sixel_helper_compute_depth(int pixelformat) +{ + int depth = (-1); /* unknown */ + + switch (pixelformat) { + case SIXEL_PIXELFORMAT_ARGB8888: + case SIXEL_PIXELFORMAT_RGBA8888: + case SIXEL_PIXELFORMAT_ABGR8888: + case SIXEL_PIXELFORMAT_BGRA8888: + depth = 4; + break; + case SIXEL_PIXELFORMAT_RGB888: + case SIXEL_PIXELFORMAT_BGR888: + depth = 3; + break; + case SIXEL_PIXELFORMAT_RGB555: + case SIXEL_PIXELFORMAT_RGB565: + case SIXEL_PIXELFORMAT_BGR555: + case SIXEL_PIXELFORMAT_BGR565: + case SIXEL_PIXELFORMAT_AG88: + case SIXEL_PIXELFORMAT_GA88: + depth = 2; + break; + case SIXEL_PIXELFORMAT_G1: + case SIXEL_PIXELFORMAT_G2: + case SIXEL_PIXELFORMAT_G4: + case SIXEL_PIXELFORMAT_G8: + case SIXEL_PIXELFORMAT_PAL1: + case SIXEL_PIXELFORMAT_PAL2: + case SIXEL_PIXELFORMAT_PAL4: + case SIXEL_PIXELFORMAT_PAL8: + depth = 1; + break; + default: + break; + } + + return depth; +} + + +static void +expand_rgb(unsigned char *dst, + unsigned char const *src, + int width, int height, + int pixelformat, int depth) +{ + int x; + int y; + int dst_offset; + int src_offset; + unsigned char r, g, b; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + src_offset = depth * (y * width + x); + dst_offset = 3 * (y * width + x); + get_rgb(src + src_offset, pixelformat, depth, &r, &g, &b); + + *(dst + dst_offset + 0) = r; + *(dst + dst_offset + 1) = g; + *(dst + dst_offset + 2) = b; + } + } +} + + +static SIXELSTATUS +expand_palette(unsigned char *dst, unsigned char const *src, + int width, int height, int const pixelformat) +{ + SIXELSTATUS status = SIXEL_FALSE; + int x; + int y; + int i; + int bpp; /* bit per plane */ + + switch (pixelformat) { + case SIXEL_PIXELFORMAT_PAL1: + case SIXEL_PIXELFORMAT_G1: + bpp = 1; + break; + case SIXEL_PIXELFORMAT_PAL2: + case SIXEL_PIXELFORMAT_G2: + bpp = 2; + break; + case SIXEL_PIXELFORMAT_PAL4: + case SIXEL_PIXELFORMAT_G4: + bpp = 4; + break; + case SIXEL_PIXELFORMAT_PAL8: + case SIXEL_PIXELFORMAT_G8: + for (i = 0; i < width * height; ++i, ++src) { + *dst++ = *src; + } + status = SIXEL_OK; + goto end; + default: + status = SIXEL_BAD_ARGUMENT; + sixel_helper_set_additional_message( + "expand_palette: invalid pixelformat."); + goto end; + } + +#if HAVE_DEBUG + fprintf(stderr, "expanding PAL%d to PAL8...\n", bpp); +#endif + + for (y = 0; y < height; ++y) { + for (x = 0; x < width * bpp / 8; ++x) { + for (i = 0; i < 8 / bpp; ++i) { + *dst++ = *src >> (8 / bpp - 1 - i) * bpp & ((1 << bpp) - 1); + } + src++; + } + x = width - x * 8 / bpp; + if (x > 0) { + for (i = 0; i < x; ++i) { + *dst++ = *src >> (8 - (i + 1) * bpp) & ((1 << bpp) - 1); + } + src++; + } + } + + status = SIXEL_OK; + +end: + return status; +} + + +SIXELAPI SIXELSTATUS +sixel_helper_normalize_pixelformat( + unsigned char /* out */ *dst, /* destination buffer */ + int /* out */ *dst_pixelformat, /* converted pixelformat */ + unsigned char const /* in */ *src, /* source pixels */ + int /* in */ src_pixelformat, /* format of source image */ + int /* in */ width, /* width of source image */ + int /* in */ height) /* height of source image */ +{ + SIXELSTATUS status = SIXEL_FALSE; + + switch (src_pixelformat) { + case SIXEL_PIXELFORMAT_G8: + expand_rgb(dst, src, width, height, src_pixelformat, 1); + *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + break; + case SIXEL_PIXELFORMAT_RGB565: + case SIXEL_PIXELFORMAT_RGB555: + case SIXEL_PIXELFORMAT_BGR565: + case SIXEL_PIXELFORMAT_BGR555: + case SIXEL_PIXELFORMAT_GA88: + case SIXEL_PIXELFORMAT_AG88: + expand_rgb(dst, src, width, height, src_pixelformat, 2); + *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + break; + case SIXEL_PIXELFORMAT_RGB888: + case SIXEL_PIXELFORMAT_BGR888: + expand_rgb(dst, src, width, height, src_pixelformat, 3); + *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + break; + case SIXEL_PIXELFORMAT_RGBA8888: + case SIXEL_PIXELFORMAT_ARGB8888: + case SIXEL_PIXELFORMAT_BGRA8888: + case SIXEL_PIXELFORMAT_ABGR8888: + expand_rgb(dst, src, width, height, src_pixelformat, 4); + *dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + break; + case SIXEL_PIXELFORMAT_PAL1: + case SIXEL_PIXELFORMAT_PAL2: + case SIXEL_PIXELFORMAT_PAL4: + *dst_pixelformat = SIXEL_PIXELFORMAT_PAL8; + status = expand_palette(dst, src, width, height, src_pixelformat); + if (SIXEL_FAILED(status)) { + goto end; + } + break; + case SIXEL_PIXELFORMAT_G1: + case SIXEL_PIXELFORMAT_G2: + case SIXEL_PIXELFORMAT_G4: + *dst_pixelformat = SIXEL_PIXELFORMAT_G8; + status = expand_palette(dst, src, width, height, src_pixelformat); + if (SIXEL_FAILED(status)) { + goto end; + } + break; + case SIXEL_PIXELFORMAT_PAL8: + memcpy(dst, src, (size_t)(width * height)); + *dst_pixelformat = src_pixelformat; + break; + default: + status = SIXEL_BAD_ARGUMENT; + goto end; + } + + status = SIXEL_OK; + +end: + return status; +} + + +#if HAVE_TESTS +static int +test1(void) +{ + unsigned char dst[3]; + int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + int src_pixelformat = SIXEL_PIXELFORMAT_RGB888; + unsigned char src[] = { 0x46, 0xf3, 0xe5 }; + int ret = 0; + + int nret = EXIT_FAILURE; + + ret = sixel_helper_normalize_pixelformat(dst, + &dst_pixelformat, + src, + src_pixelformat, + 1, + 1); + if (ret != 0) { + goto error; + } + if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) { + goto error; + } + if ((dst[0] << 16 | dst[1] << 8 | dst[2]) != (src[0] << 16 | src[1] << 8 | src[2])) { + goto error; + } + return EXIT_SUCCESS; + +error: + perror("test1"); + return nret; +} + + +static int +test2(void) +{ + unsigned char dst[3]; + int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + int src_pixelformat = SIXEL_PIXELFORMAT_RGB555; + unsigned char src[] = { 0x47, 0x9c }; + int ret = 0; + + int nret = EXIT_FAILURE; + + ret = sixel_helper_normalize_pixelformat(dst, + &dst_pixelformat, + src, + src_pixelformat, + 1, + 1); + if (ret != 0) { + goto error; + } + if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) { + goto error; + } + if ((dst[0] >> 3 << 10 | dst[1] >> 3 << 5 | dst[2] >> 3) != (src[0] << 8 | src[1])) { + goto error; + } + return EXIT_SUCCESS; + +error: + perror("test2"); + return nret; +} + + +static int +test3(void) +{ + unsigned char dst[3]; + int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + int src_pixelformat = SIXEL_PIXELFORMAT_RGB565; + unsigned char src[] = { 0x47, 0x9c }; + int ret = 0; + + int nret = EXIT_FAILURE; + + ret = sixel_helper_normalize_pixelformat(dst, + &dst_pixelformat, + src, + src_pixelformat, + 1, + 1); + if (ret != 0) { + goto error; + } + if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) { + goto error; + } + if ((dst[0] >> 3 << 11 | dst[1] >> 2 << 5 | dst[2] >> 3) != (src[0] << 8 | src[1])) { + goto error; + } + return EXIT_SUCCESS; + +error: + perror("test3"); + return nret; +} + + +static int +test4(void) +{ + unsigned char dst[3]; + int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + int src_pixelformat = SIXEL_PIXELFORMAT_BGR888; + unsigned char src[] = { 0x46, 0xf3, 0xe5 }; + int ret = 0; + + int nret = EXIT_FAILURE; + + ret = sixel_helper_normalize_pixelformat(dst, + &dst_pixelformat, + src, + src_pixelformat, + 1, + 1); + if (ret != 0) { + goto error; + } + if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) { + goto error; + } + if ((dst[2] << 16 | dst[1] << 8 | dst[0]) != (src[0] << 16 | src[1] << 8 | src[2])) { + goto error; + } + return EXIT_SUCCESS; + +error: + perror("test4"); + return nret; +} + + +static int +test5(void) +{ + unsigned char dst[3]; + int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + int src_pixelformat = SIXEL_PIXELFORMAT_BGR555; + unsigned char src[] = { 0x23, 0xc8 }; + int ret = 0; + + int nret = EXIT_FAILURE; + + ret = sixel_helper_normalize_pixelformat(dst, + &dst_pixelformat, + src, + src_pixelformat, + 1, + 1); + if (ret != 0) { + goto error; + } + if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) { + goto error; + } + if ((dst[2] >> 3 << 10 | dst[1] >> 3 << 5 | dst[0] >> 3) != (src[0] << 8 | src[1])) { + goto error; + } + return EXIT_SUCCESS; + +error: + perror("test5"); + return nret; +} + + +static int +test6(void) +{ + unsigned char dst[3]; + int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + int src_pixelformat = SIXEL_PIXELFORMAT_BGR565; + unsigned char src[] = { 0x47, 0x88 }; + int ret = 0; + + int nret = EXIT_FAILURE; + + ret = sixel_helper_normalize_pixelformat(dst, + &dst_pixelformat, + src, + src_pixelformat, + 1, + 1); + if (ret != 0) { + goto error; + } + if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) { + goto error; + } + if ((dst[2] >> 3 << 11 | dst[1] >> 2 << 5 | dst[0] >> 3) != (src[0] << 8 | src[1])) { + goto error; + } + return EXIT_SUCCESS; + +error: + perror("test6"); + return nret; +} + + +static int +test7(void) +{ + unsigned char dst[3]; + int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + int src_pixelformat = SIXEL_PIXELFORMAT_AG88; + unsigned char src[] = { 0x47, 0x88 }; + int ret = 0; + + int nret = EXIT_FAILURE; + + ret = sixel_helper_normalize_pixelformat(dst, + &dst_pixelformat, + src, + src_pixelformat, + 1, + 1); + if (ret != 0) { + goto error; + } + if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) { + goto error; + } + if (dst[0] != src[1]) { + goto error; + } + return EXIT_SUCCESS; + +error: + perror("test7"); + return nret; +} + + +static int +test8(void) +{ + unsigned char dst[3]; + int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + int src_pixelformat = SIXEL_PIXELFORMAT_GA88; + unsigned char src[] = { 0x47, 0x88 }; + int ret = 0; + + int nret = EXIT_FAILURE; + + ret = sixel_helper_normalize_pixelformat(dst, + &dst_pixelformat, + src, + src_pixelformat, + 1, + 1); + if (ret != 0) { + goto error; + } + if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) { + goto error; + } + if (dst[0] != src[0]) { + goto error; + } + return EXIT_SUCCESS; + +error: + perror("test8"); + return nret; +} + + +static int +test9(void) +{ + unsigned char dst[3]; + int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + int src_pixelformat = SIXEL_PIXELFORMAT_RGBA8888; + unsigned char src[] = { 0x46, 0xf3, 0xe5, 0xf0 }; + int ret = 0; + + int nret = EXIT_FAILURE; + + ret = sixel_helper_normalize_pixelformat(dst, + &dst_pixelformat, + src, + src_pixelformat, + 1, + 1); + if (ret != 0) { + goto error; + } + if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) { + goto error; + } + if (dst[0] != src[0]) { + goto error; + } + if (dst[1] != src[1]) { + goto error; + } + if (dst[2] != src[2]) { + goto error; + } + return EXIT_SUCCESS; + +error: + perror("test8"); + return nret; +} + + +static int +test10(void) +{ + unsigned char dst[3]; + int dst_pixelformat = SIXEL_PIXELFORMAT_RGB888; + int src_pixelformat = SIXEL_PIXELFORMAT_ARGB8888; + unsigned char src[] = { 0x46, 0xf3, 0xe5, 0xf0 }; + int ret = 0; + + int nret = EXIT_FAILURE; + + ret = sixel_helper_normalize_pixelformat(dst, + &dst_pixelformat, + src, + src_pixelformat, + 1, + 1); + if (ret != 0) { + goto error; + } + if (dst_pixelformat != SIXEL_PIXELFORMAT_RGB888) { + goto error; + } + if (dst[0] != src[1]) { + goto error; + } + if (dst[1] != src[2]) { + goto error; + } + if (dst[2] != src[3]) { + goto error; + } + return EXIT_SUCCESS; + +error: + perror("test8"); + return nret; +} + + +SIXELAPI int +sixel_pixelformat_tests_main(void) +{ + int nret = EXIT_FAILURE; + size_t i; + typedef int (* testcase)(void); + + static testcase const testcases[] = { + test1, + test2, + test3, + test4, + test5, + test6, + test7, + test8, + test9, + test10, + }; + + for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) { + nret = testcases[i](); + if (nret != EXIT_SUCCESS) { + goto error; + } + } + + nret = EXIT_SUCCESS; + +error: + return nret; +} +#endif /* HAVE_TESTS */ + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ diff --git a/src/3rdparty/sixel/quant.c b/src/3rdparty/sixel/quant.c new file mode 100644 index 000000000..f730b0028 --- /dev/null +++ b/src/3rdparty/sixel/quant.c @@ -0,0 +1,1549 @@ +/* + * + * mediancut algorithm implementation is imported from pnmcolormap.c + * in netpbm library. + * http://netpbm.sourceforge.net/ + * + * ******************************************************************************* + * original license block of pnmcolormap.c + * ******************************************************************************* + * + * Derived from ppmquant, originally by Jef Poskanzer. + * + * Copyright (C) 1989, 1991 by Jef Poskanzer. + * Copyright (C) 2001 by Bryan Henderson. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. This software is provided "as is" without express or + * implied warranty. + * + * ****************************************************************************** + * + * Copyright (c) 2014-2018 Hayaki Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + */ + +#include "config.h" + +/* STDC_HEADERS */ +#include +#include + +#if HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#if HAVE_MATH_H +#include +#endif /* HAVE_MATH_H */ +#if HAVE_LIMITS_H +# include +#endif /* HAVE_MATH_H */ +#if HAVE_INTTYPES_H +# include +#endif /* HAVE_MATH_H */ + +#include "quant.h" + +#if HAVE_DEBUG +#define quant_trace fprintf +#else +static inline void quant_trace(FILE *f, ...) { (void) f; } +#endif + +/***************************************************************************** + * + * quantization + * + *****************************************************************************/ + +typedef struct box* boxVector; +struct box { + unsigned int ind; + unsigned int colors; + unsigned int sum; +}; + +typedef unsigned long sample; +typedef sample * tuple; + +struct tupleint { + /* An ordered pair of a tuple value and an integer, such as you + would find in a tuple table or tuple hash. + Note that this is a variable length structure. + */ + unsigned int value; + sample tuple[1]; + /* This is actually a variable size array -- its size is the + depth of the tuple in question. Some compilers do not let us + declare a variable length array. + */ +}; +typedef struct tupleint ** tupletable; + +typedef struct { + unsigned int size; + tupletable table; +} tupletable2; + +static unsigned int compareplanePlane; + /* This is a parameter to compareplane(). We use this global variable + so that compareplane() can be called by qsort(), to compare two + tuples. qsort() doesn't pass any arguments except the two tuples. + */ +static int +compareplane(const void * const arg1, + const void * const arg2) +{ + int lhs, rhs; + + typedef const struct tupleint * const * const sortarg; + sortarg comparandPP = (sortarg) arg1; + sortarg comparatorPP = (sortarg) arg2; + lhs = (int)(*comparandPP)->tuple[compareplanePlane]; + rhs = (int)(*comparatorPP)->tuple[compareplanePlane]; + + return lhs - rhs; +} + + +static int +sumcompare(const void * const b1, const void * const b2) +{ + return (int)((boxVector)b2)->sum - (int)((boxVector)b1)->sum; +} + + +static SIXELSTATUS +alloctupletable( + tupletable /* out */ *result, + unsigned int const /* in */ depth, + unsigned int const /* in */ size, + sixel_allocator_t /* in */ *allocator) +{ + SIXELSTATUS status = SIXEL_FALSE; + enum { message_buffer_size = 256 }; + char message[message_buffer_size]; + int nwrite; + unsigned int mainTableSize; + unsigned int tupleIntSize; + unsigned int allocSize; + void * pool; + tupletable tbl; + unsigned int i; + + if (UINT_MAX / sizeof(struct tupleint) < size) { + nwrite = sprintf(message, + "size %u is too big for arithmetic", + size); + if (nwrite > 0) { + sixel_helper_set_additional_message(message); + } + status = SIXEL_RUNTIME_ERROR; + goto end; + } + + mainTableSize = size * sizeof(struct tupleint *); + tupleIntSize = sizeof(struct tupleint) - sizeof(sample) + + depth * sizeof(sample); + + /* To save the enormous amount of time it could take to allocate + each individual tuple, we do a trick here and allocate everything + as a single malloc block and suballocate internally. + */ + if ((UINT_MAX - mainTableSize) / tupleIntSize < size) { + nwrite = sprintf(message, + "size %u is too big for arithmetic", + size); + if (nwrite > 0) { + sixel_helper_set_additional_message(message); + } + status = SIXEL_RUNTIME_ERROR; + goto end; + } + + allocSize = mainTableSize + size * tupleIntSize; + + pool = sixel_allocator_malloc(allocator, allocSize); + if (pool == NULL) { + sprintf(message, + "unable to allocate %u bytes for a %u-entry " + "tuple table", + allocSize, size); + sixel_helper_set_additional_message(message); + status = SIXEL_BAD_ALLOCATION; + goto end; + } + tbl = (tupletable) pool; + + for (i = 0; i < size; ++i) + tbl[i] = (struct tupleint *) + ((char*)pool + mainTableSize + i * tupleIntSize); + + *result = tbl; + + status = SIXEL_OK; + +end: + return status; +} + + +/* +** Here is the fun part, the median-cut colormap generator. This is based +** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer +** Display", SIGGRAPH '82 Proceedings, page 297. +*/ + +static tupletable2 +newColorMap(unsigned int const newcolors, unsigned int const depth, sixel_allocator_t *allocator) +{ + SIXELSTATUS status = SIXEL_FALSE; + tupletable2 colormap; + unsigned int i; + + colormap.size = 0; + status = alloctupletable(&colormap.table, depth, newcolors, allocator); + if (SIXEL_FAILED(status)) { + goto end; + } + if (colormap.table) { + for (i = 0; i < newcolors; ++i) { + unsigned int plane; + for (plane = 0; plane < depth; ++plane) + colormap.table[i]->tuple[plane] = 0; + } + colormap.size = newcolors; + } + +end: + return colormap; +} + + +static boxVector +newBoxVector( + unsigned int const /* in */ colors, + unsigned int const /* in */ sum, + unsigned int const /* in */ newcolors, + sixel_allocator_t /* in */ *allocator) +{ + boxVector bv; + + bv = (boxVector)sixel_allocator_malloc(allocator, + sizeof(struct box) * (size_t)newcolors); + if (bv == NULL) { + quant_trace(stderr, "out of memory allocating box vector table\n"); + return NULL; + } + + /* Set up the initial box. */ + bv[0].ind = 0; + bv[0].colors = colors; + bv[0].sum = sum; + + return bv; +} + + +static void +findBoxBoundaries(tupletable2 const colorfreqtable, + unsigned int const depth, + unsigned int const boxStart, + unsigned int const boxSize, + sample minval[], + sample maxval[]) +{ +/*---------------------------------------------------------------------------- + Go through the box finding the minimum and maximum of each + component - the boundaries of the box. +-----------------------------------------------------------------------------*/ + unsigned int plane; + unsigned int i; + + for (plane = 0; plane < depth; ++plane) { + minval[plane] = colorfreqtable.table[boxStart]->tuple[plane]; + maxval[plane] = minval[plane]; + } + + for (i = 1; i < boxSize; ++i) { + for (plane = 0; plane < depth; ++plane) { + sample const v = colorfreqtable.table[boxStart + i]->tuple[plane]; + if (v < minval[plane]) minval[plane] = v; + if (v > maxval[plane]) maxval[plane] = v; + } + } +} + + + +static unsigned int +largestByNorm(sample minval[], sample maxval[], unsigned int const depth) +{ + + unsigned int largestDimension; + unsigned int plane; + sample largestSpreadSoFar; + + largestSpreadSoFar = 0; + largestDimension = 0; + for (plane = 0; plane < depth; ++plane) { + sample const spread = maxval[plane]-minval[plane]; + if (spread > largestSpreadSoFar) { + largestDimension = plane; + largestSpreadSoFar = spread; + } + } + return largestDimension; +} + + + +static unsigned int +largestByLuminosity(sample minval[], sample maxval[], unsigned int const depth) +{ +/*---------------------------------------------------------------------------- + This subroutine presumes that the tuple type is either + BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3). + To save time, we don't actually check it. +-----------------------------------------------------------------------------*/ + unsigned int retval; + + double lumin_factor[3] = {0.2989, 0.5866, 0.1145}; + + if (depth == 1) { + retval = 0; + } else { + /* An RGB tuple */ + unsigned int largestDimension; + unsigned int plane; + double largestSpreadSoFar; + + largestSpreadSoFar = 0.0; + largestDimension = 0; + + for (plane = 0; plane < 3; ++plane) { + double const spread = + lumin_factor[plane] * (maxval[plane]-minval[plane]); + if (spread > largestSpreadSoFar) { + largestDimension = plane; + largestSpreadSoFar = spread; + } + } + retval = largestDimension; + } + return retval; +} + + + +static void +centerBox(unsigned int const boxStart, + unsigned int const boxSize, + tupletable2 const colorfreqtable, + unsigned int const depth, + tuple const newTuple) +{ + + unsigned int plane; + sample minval, maxval; + unsigned int i; + + for (plane = 0; plane < depth; ++plane) { + minval = maxval = colorfreqtable.table[boxStart]->tuple[plane]; + + for (i = 1; i < boxSize; ++i) { + sample v = colorfreqtable.table[boxStart + i]->tuple[plane]; + minval = minval < v ? minval: v; + maxval = maxval > v ? maxval: v; + } + newTuple[plane] = (minval + maxval) / 2; + } +} + + + +static void +averageColors(unsigned int const boxStart, + unsigned int const boxSize, + tupletable2 const colorfreqtable, + unsigned int const depth, + tuple const newTuple) +{ + unsigned int plane; + sample sum; + unsigned int i; + + for (plane = 0; plane < depth; ++plane) { + sum = 0; + + for (i = 0; i < boxSize; ++i) { + sum += colorfreqtable.table[boxStart + i]->tuple[plane]; + } + + newTuple[plane] = sum / boxSize; + } +} + + + +static void +averagePixels(unsigned int const boxStart, + unsigned int const boxSize, + tupletable2 const colorfreqtable, + unsigned int const depth, + tuple const newTuple) +{ + + unsigned int n; + /* Number of tuples represented by the box */ + unsigned int plane; + unsigned int i; + + /* Count the tuples in question */ + n = 0; /* initial value */ + for (i = 0; i < boxSize; ++i) { + n += (unsigned int)colorfreqtable.table[boxStart + i]->value; + } + + for (plane = 0; plane < depth; ++plane) { + sample sum; + + sum = 0; + + for (i = 0; i < boxSize; ++i) { + sum += colorfreqtable.table[boxStart + i]->tuple[plane] + * (unsigned int)colorfreqtable.table[boxStart + i]->value; + } + + newTuple[plane] = sum / n; + } +} + + + +static tupletable2 +colormapFromBv(unsigned int const newcolors, + boxVector const bv, + unsigned int const boxes, + tupletable2 const colorfreqtable, + unsigned int const depth, + int const methodForRep, + sixel_allocator_t *allocator) +{ + /* + ** Ok, we've got enough boxes. Now choose a representative color for + ** each box. There are a number of possible ways to make this choice. + ** One would be to choose the center of the box; this ignores any structure + ** within the boxes. Another method would be to average all the colors in + ** the box - this is the method specified in Heckbert's paper. A third + ** method is to average all the pixels in the box. + */ + tupletable2 colormap; + unsigned int bi; + + colormap = newColorMap(newcolors, depth, allocator); + if (!colormap.size) { + return colormap; + } + + for (bi = 0; bi < boxes; ++bi) { + switch (methodForRep) { + case SIXEL_REP_CENTER_BOX: + centerBox(bv[bi].ind, bv[bi].colors, + colorfreqtable, depth, + colormap.table[bi]->tuple); + break; + case SIXEL_REP_AVERAGE_COLORS: + averageColors(bv[bi].ind, bv[bi].colors, + colorfreqtable, depth, + colormap.table[bi]->tuple); + break; + case SIXEL_REP_AVERAGE_PIXELS: + averagePixels(bv[bi].ind, bv[bi].colors, + colorfreqtable, depth, + colormap.table[bi]->tuple); + break; + default: + quant_trace(stderr, "Internal error: " + "invalid value of methodForRep: %d\n", + methodForRep); + } + } + return colormap; +} + + +static SIXELSTATUS +splitBox(boxVector const bv, + unsigned int *const boxesP, + unsigned int const bi, + tupletable2 const colorfreqtable, + unsigned int const depth, + int const methodForLargest) +{ +/*---------------------------------------------------------------------------- + Split Box 'bi' in the box vector bv (so that bv contains one more box + than it did as input). Split it so that each new box represents about + half of the pixels in the distribution given by 'colorfreqtable' for + the colors in the original box, but with distinct colors in each of the + two new boxes. + + Assume the box contains at least two colors. +-----------------------------------------------------------------------------*/ + SIXELSTATUS status = SIXEL_FALSE; + unsigned int const boxStart = bv[bi].ind; + unsigned int const boxSize = bv[bi].colors; + unsigned int const sm = bv[bi].sum; + + enum { max_depth= 16 }; + sample minval[max_depth]; + sample maxval[max_depth]; + + /* assert(max_depth >= depth); */ + + unsigned int largestDimension; + /* number of the plane with the largest spread */ + unsigned int medianIndex; + unsigned int lowersum; + /* Number of pixels whose value is "less than" the median */ + + findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize, + minval, maxval); + + /* Find the largest dimension, and sort by that component. I have + included two methods for determining the "largest" dimension; + first by simply comparing the range in RGB space, and second by + transforming into luminosities before the comparison. + */ + switch (methodForLargest) { + case SIXEL_LARGE_NORM: + largestDimension = largestByNorm(minval, maxval, depth); + break; + case SIXEL_LARGE_LUM: + largestDimension = largestByLuminosity(minval, maxval, depth); + break; + default: + sixel_helper_set_additional_message( + "Internal error: invalid value of methodForLargest."); + status = SIXEL_LOGIC_ERROR; + goto end; + } + + /* TODO: I think this sort should go after creating a box, + not before splitting. Because you need the sort to use + the SIXEL_REP_CENTER_BOX method of choosing a color to + represent the final boxes + */ + + /* Set the gross global variable 'compareplanePlane' as a + parameter to compareplane(), which is called by qsort(). + */ + compareplanePlane = largestDimension; + qsort((char*) &colorfreqtable.table[boxStart], boxSize, + sizeof(colorfreqtable.table[boxStart]), + compareplane); + + { + /* Now find the median based on the counts, so that about half + the pixels (not colors, pixels) are in each subdivision. */ + + unsigned int i; + + lowersum = colorfreqtable.table[boxStart]->value; /* initial value */ + for (i = 1; i < boxSize - 1 && lowersum < sm / 2; ++i) { + lowersum += colorfreqtable.table[boxStart + i]->value; + } + medianIndex = i; + } + /* Split the box, and sort to bring the biggest boxes to the top. */ + + bv[bi].colors = medianIndex; + bv[bi].sum = lowersum; + bv[*boxesP].ind = boxStart + medianIndex; + bv[*boxesP].colors = boxSize - medianIndex; + bv[*boxesP].sum = sm - lowersum; + ++(*boxesP); + qsort((char*) bv, *boxesP, sizeof(struct box), sumcompare); + + status = SIXEL_OK; + +end: + return status; +} + + + +static SIXELSTATUS +mediancut(tupletable2 const colorfreqtable, + unsigned int const depth, + unsigned int const newcolors, + int const methodForLargest, + int const methodForRep, + tupletable2 *const colormapP, + sixel_allocator_t *allocator) +{ +/*---------------------------------------------------------------------------- + Compute a set of only 'newcolors' colors that best represent an + image whose pixels are summarized by the histogram + 'colorfreqtable'. Each tuple in that table has depth 'depth'. + colorfreqtable.table[i] tells the number of pixels in the subject image + have a particular color. + + As a side effect, sort 'colorfreqtable'. +-----------------------------------------------------------------------------*/ + boxVector bv; + unsigned int bi; + unsigned int boxes; + int multicolorBoxesExist; + unsigned int i; + unsigned int sum; + SIXELSTATUS status = SIXEL_FALSE; + + sum = 0; + + for (i = 0; i < colorfreqtable.size; ++i) { + sum += colorfreqtable.table[i]->value; + } + + /* There is at least one box that contains at least 2 colors; ergo, + there is more splitting we can do. */ + bv = newBoxVector(colorfreqtable.size, sum, newcolors, allocator); + if (bv == NULL) { + goto end; + } + boxes = 1; + multicolorBoxesExist = (colorfreqtable.size > 1); + + /* Main loop: split boxes until we have enough. */ + while (boxes < newcolors && multicolorBoxesExist) { + /* Find the first splittable box. */ + for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi) + ; + if (bi >= boxes) { + multicolorBoxesExist = 0; + } else { + status = splitBox(bv, &boxes, bi, + colorfreqtable, depth, + methodForLargest); + if (SIXEL_FAILED(status)) { + goto end; + } + } + } + *colormapP = colormapFromBv(newcolors, bv, boxes, + colorfreqtable, depth, + methodForRep, allocator); + + sixel_allocator_free(allocator, bv); + + status = SIXEL_OK; + +end: + return status; +} + + +static unsigned int +computeHash(unsigned char const *data, unsigned int const depth) +{ + unsigned int hash = 0; + unsigned int n; + + for (n = 0; n < depth; n++) { + hash |= (unsigned int)(data[depth - 1 - n] >> 3) << n * 5; + } + + return hash; +} + + +static SIXELSTATUS +computeHistogram(unsigned char const /* in */ *data, + unsigned int /* in */ length, + unsigned long const /* in */ depth, + tupletable2 * const /* out */ colorfreqtableP, + int const /* in */ qualityMode, + sixel_allocator_t /* in */ *allocator) +{ + SIXELSTATUS status = SIXEL_FALSE; + typedef unsigned short unit_t; + unsigned int i, n; + unit_t *histogram = NULL; + unit_t *refmap = NULL; + unit_t *ref; + unit_t *it; + unsigned int bucket_index; + unsigned int step; + unsigned int max_sample; + + switch (qualityMode) { + case SIXEL_QUALITY_LOW: + max_sample = 18383; + break; + case SIXEL_QUALITY_HIGH: + max_sample = 1118383; + break; + case SIXEL_QUALITY_FULL: + default: + max_sample = 4003079; + break; + } + + step = length / depth / max_sample * depth; + if (step <= 0) { + step = depth; + } + + quant_trace(stderr, "making histogram...\n"); + + histogram = (unit_t *)sixel_allocator_calloc(allocator, + (size_t)(1 << depth * 5), + sizeof(unit_t)); + if (histogram == NULL) { + sixel_helper_set_additional_message( + "unable to allocate memory for histogram."); + status = SIXEL_BAD_ALLOCATION; + goto end; + } + it = ref = refmap + = (unsigned short *)sixel_allocator_malloc(allocator, + (size_t)(1 << depth * 5) * sizeof(unit_t)); + if (!it) { + sixel_helper_set_additional_message( + "unable to allocate memory for lookup table."); + status = SIXEL_BAD_ALLOCATION; + goto end; + } + + for (i = 0; i < length; i += step) { + bucket_index = computeHash(data + i, 3); + if (histogram[bucket_index] == 0) { + *ref++ = bucket_index; + } + if (histogram[bucket_index] < (unsigned int)(1 << sizeof(unsigned short) * 8) - 1) { + histogram[bucket_index]++; + } + } + + colorfreqtableP->size = (unsigned int)(ref - refmap); + + status = alloctupletable(&colorfreqtableP->table, depth, (unsigned int)(ref - refmap), allocator); + if (SIXEL_FAILED(status)) { + goto end; + } + for (i = 0; i < colorfreqtableP->size; ++i) { + if (histogram[refmap[i]] > 0) { + colorfreqtableP->table[i]->value = histogram[refmap[i]]; + for (n = 0; n < depth; n++) { + colorfreqtableP->table[i]->tuple[depth - 1 - n] + = (sample)((*it >> n * 5 & 0x1f) << 3); + } + } + it++; + } + + quant_trace(stderr, "%u colors found\n", colorfreqtableP->size); + + status = SIXEL_OK; + +end: + sixel_allocator_free(allocator, refmap); + sixel_allocator_free(allocator, histogram); + + return status; +} + + +static int +computeColorMapFromInput(unsigned char const *data, + unsigned int const length, + unsigned int const depth, + unsigned int const reqColors, + int const methodForLargest, + int const methodForRep, + int const qualityMode, + tupletable2 * const colormapP, + unsigned int *origcolors, + sixel_allocator_t *allocator) +{ +/*---------------------------------------------------------------------------- + Produce a colormap containing the best colors to represent the + image stream in file 'ifP'. Figure it out using the median cut + technique. + + The colormap will have 'reqcolors' or fewer colors in it, unless + 'allcolors' is true, in which case it will have all the colors that + are in the input. + + The colormap has the same maxval as the input. + + Put the colormap in newly allocated storage as a tupletable2 + and return its address as *colormapP. Return the number of colors in + it as *colorsP and its maxval as *colormapMaxvalP. + + Return the characteristics of the input file as + *formatP and *freqPamP. (This information is not really + relevant to our colormap mission; just a fringe benefit). +-----------------------------------------------------------------------------*/ + SIXELSTATUS status = SIXEL_FALSE; + tupletable2 colorfreqtable = {0, NULL}; + unsigned int i; + unsigned int n; + + status = computeHistogram(data, length, depth, + &colorfreqtable, qualityMode, allocator); + if (SIXEL_FAILED(status)) { + goto end; + } + if (origcolors) { + *origcolors = colorfreqtable.size; + } + + if (colorfreqtable.size <= reqColors) { + quant_trace(stderr, + "Image already has few enough colors (<=%d). " + "Keeping same colors.\n", reqColors); + /* *colormapP = colorfreqtable; */ + colormapP->size = colorfreqtable.size; + status = alloctupletable(&colormapP->table, depth, colorfreqtable.size, allocator); + if (SIXEL_FAILED(status)) { + goto end; + } + for (i = 0; i < colorfreqtable.size; ++i) { + colormapP->table[i]->value = colorfreqtable.table[i]->value; + for (n = 0; n < depth; ++n) { + colormapP->table[i]->tuple[n] = colorfreqtable.table[i]->tuple[n]; + } + } + } else { + quant_trace(stderr, "choosing %d colors...\n", reqColors); + status = mediancut(colorfreqtable, depth, reqColors, + methodForLargest, methodForRep, colormapP, allocator); + if (SIXEL_FAILED(status)) { + goto end; + } + quant_trace(stderr, "%d colors are choosed.\n", colorfreqtable.size); + } + + status = SIXEL_OK; + +end: + sixel_allocator_free(allocator, colorfreqtable.table); + return status; +} + + +/* diffuse error energy to surround pixels */ +static void +error_diffuse(unsigned char /* in */ *data, /* base address of pixel buffer */ + int /* in */ pos, /* address of the destination pixel */ + int /* in */ depth, /* color depth in bytes */ + int /* in */ error, /* error energy */ + int /* in */ numerator, /* numerator of diffusion coefficient */ + int /* in */ denominator /* denominator of diffusion coefficient */) +{ + int c; + + data += pos * depth; + + c = *data + error * numerator / denominator; + if (c < 0) { + c = 0; + } + if (c >= 1 << 8) { + c = (1 << 8) - 1; + } + *data = (unsigned char)c; +} + + +static void +diffuse_none(unsigned char *data, int width, int height, + int x, int y, int depth, int error) +{ + /* unused */ (void) data; + /* unused */ (void) width; + /* unused */ (void) height; + /* unused */ (void) x; + /* unused */ (void) y; + /* unused */ (void) depth; + /* unused */ (void) error; +} + + +static void +diffuse_fs(unsigned char *data, int width, int height, + int x, int y, int depth, int error) +{ + int pos; + + pos = y * width + x; + + /* Floyd Steinberg Method + * curr 7/16 + * 3/16 5/48 1/16 + */ + if (x < width - 1 && y < height - 1) { + /* add error to the right cell */ + error_diffuse(data, pos + width * 0 + 1, depth, error, 7, 16); + /* add error to the left-bottom cell */ + error_diffuse(data, pos + width * 1 - 1, depth, error, 3, 16); + /* add error to the bottom cell */ + error_diffuse(data, pos + width * 1 + 0, depth, error, 5, 16); + /* add error to the right-bottom cell */ + error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 16); + } +} + + +static void +diffuse_atkinson(unsigned char *data, int width, int height, + int x, int y, int depth, int error) +{ + int pos; + + pos = y * width + x; + + /* Atkinson's Method + * curr 1/8 1/8 + * 1/8 1/8 1/8 + * 1/8 + */ + if (y < height - 2) { + /* add error to the right cell */ + error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 8); + /* add error to the 2th right cell */ + error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 8); + /* add error to the left-bottom cell */ + error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 8); + /* add error to the bottom cell */ + error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 8); + /* add error to the right-bottom cell */ + error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 8); + /* add error to the 2th bottom cell */ + error_diffuse(data, pos + width * 2 + 0, depth, error, 1, 8); + } +} + + +static void +diffuse_jajuni(unsigned char *data, int width, int height, + int x, int y, int depth, int error) +{ + int pos; + + pos = y * width + x; + + /* Jarvis, Judice & Ninke Method + * curr 7/48 5/48 + * 3/48 5/48 7/48 5/48 3/48 + * 1/48 3/48 5/48 3/48 1/48 + */ + if (pos < (height - 2) * width - 2) { + error_diffuse(data, pos + width * 0 + 1, depth, error, 7, 48); + error_diffuse(data, pos + width * 0 + 2, depth, error, 5, 48); + error_diffuse(data, pos + width * 1 - 2, depth, error, 3, 48); + error_diffuse(data, pos + width * 1 - 1, depth, error, 5, 48); + error_diffuse(data, pos + width * 1 + 0, depth, error, 7, 48); + error_diffuse(data, pos + width * 1 + 1, depth, error, 5, 48); + error_diffuse(data, pos + width * 1 + 2, depth, error, 3, 48); + error_diffuse(data, pos + width * 2 - 2, depth, error, 1, 48); + error_diffuse(data, pos + width * 2 - 1, depth, error, 3, 48); + error_diffuse(data, pos + width * 2 + 0, depth, error, 5, 48); + error_diffuse(data, pos + width * 2 + 1, depth, error, 3, 48); + error_diffuse(data, pos + width * 2 + 2, depth, error, 1, 48); + } +} + + +static void +diffuse_stucki(unsigned char *data, int width, int height, + int x, int y, int depth, int error) +{ + int pos; + + pos = y * width + x; + + /* Stucki's Method + * curr 8/48 4/48 + * 2/48 4/48 8/48 4/48 2/48 + * 1/48 2/48 4/48 2/48 1/48 + */ + if (pos < (height - 2) * width - 2) { + error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 6); + error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 12); + error_diffuse(data, pos + width * 1 - 2, depth, error, 1, 24); + error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 12); + error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 6); + error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 12); + error_diffuse(data, pos + width * 1 + 2, depth, error, 1, 24); + error_diffuse(data, pos + width * 2 - 2, depth, error, 1, 48); + error_diffuse(data, pos + width * 2 - 1, depth, error, 1, 24); + error_diffuse(data, pos + width * 2 + 0, depth, error, 1, 12); + error_diffuse(data, pos + width * 2 + 1, depth, error, 1, 24); + error_diffuse(data, pos + width * 2 + 2, depth, error, 1, 48); + } +} + + +static void +diffuse_burkes(unsigned char *data, int width, int height, + int x, int y, int depth, int error) +{ + int pos; + + pos = y * width + x; + + /* Burkes' Method + * curr 4/16 2/16 + * 1/16 2/16 4/16 2/16 1/16 + */ + if (pos < (height - 1) * width - 2) { + error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 4); + error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 8); + error_diffuse(data, pos + width * 1 - 2, depth, error, 1, 16); + error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 8); + error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 4); + error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 8); + error_diffuse(data, pos + width * 1 + 2, depth, error, 1, 16); + } +} + +static float +mask_a (int x, int y, int c) +{ + return ((((x + c * 67) + y * 236) * 119) & 255 ) / 128.0 - 1.0; +} + +static float +mask_x (int x, int y, int c) +{ + return ((((x + c * 29) ^ y* 149) * 1234) & 511 ) / 256.0 - 1.0; +} + +/* lookup closest color from palette with "normal" strategy */ +static int +lookup_normal(unsigned char const * const pixel, + int const depth, + unsigned char const * const palette, + int const reqcolor, + unsigned short * const cachetable, + int const complexion) +{ + int result; + int diff; + int r; + int i; + int n; + int distant; + + result = (-1); + diff = INT_MAX; + + /* don't use cachetable in 'normal' strategy */ + (void) cachetable; + + for (i = 0; i < reqcolor; i++) { + distant = 0; + r = pixel[0] - palette[i * depth + 0]; + distant += r * r * complexion; + for (n = 1; n < depth; ++n) { + r = pixel[n] - palette[i * depth + n]; + distant += r * r; + } + if (distant < diff) { + diff = distant; + result = i; + } + } + + return result; +} + + +/* lookup closest color from palette with "fast" strategy */ +static int +lookup_fast(unsigned char const * const pixel, + int const depth, + unsigned char const * const palette, + int const reqcolor, + unsigned short * const cachetable, + int const complexion) +{ + int result; + unsigned int hash; + int diff; + int cache; + int i; + int distant; + + /* don't use depth in 'fast' strategy because it's always 3 */ + (void) depth; + + result = (-1); + diff = INT_MAX; + hash = computeHash(pixel, 3); + + cache = cachetable[hash]; + if (cache) { /* fast lookup */ + return cache - 1; + } + /* collision */ + for (i = 0; i < reqcolor; i++) { + distant = 0; +#if 0 + for (n = 0; n < 3; ++n) { + r = pixel[n] - palette[i * 3 + n]; + distant += r * r; + } +#elif 1 /* complexion correction */ + distant = (pixel[0] - palette[i * 3 + 0]) * (pixel[0] - palette[i * 3 + 0]) * complexion + + (pixel[1] - palette[i * 3 + 1]) * (pixel[1] - palette[i * 3 + 1]) + + (pixel[2] - palette[i * 3 + 2]) * (pixel[2] - palette[i * 3 + 2]) + ; +#endif + if (distant < diff) { + diff = distant; + result = i; + } + } + cachetable[hash] = result + 1; + + return result; +} + + +static int +lookup_mono_darkbg(unsigned char const * const pixel, + int const depth, + unsigned char const * const palette, + int const reqcolor, + unsigned short * const cachetable, + int const complexion) +{ + int n; + int distant; + + /* unused */ (void) palette; + /* unused */ (void) cachetable; + /* unused */ (void) complexion; + + distant = 0; + for (n = 0; n < depth; ++n) { + distant += pixel[n]; + } + return distant >= 128 * reqcolor ? 1: 0; +} + + +static int +lookup_mono_lightbg(unsigned char const * const pixel, + int const depth, + unsigned char const * const palette, + int const reqcolor, + unsigned short * const cachetable, + int const complexion) +{ + int n; + int distant; + + /* unused */ (void) palette; + /* unused */ (void) cachetable; + /* unused */ (void) complexion; + + distant = 0; + for (n = 0; n < depth; ++n) { + distant += pixel[n]; + } + return distant < 128 * reqcolor ? 1: 0; +} + + +/* choose colors using median-cut method */ +SIXELSTATUS +sixel_quant_make_palette( + unsigned char /* out */ **result, + unsigned char const /* in */ *data, + unsigned int /* in */ length, + int /* in */ pixelformat, + unsigned int /* in */ reqcolors, + unsigned int /* in */ *ncolors, + unsigned int /* in */ *origcolors, + int /* in */ methodForLargest, + int /* in */ methodForRep, + int /* in */ qualityMode, + sixel_allocator_t /* in */ *allocator) +{ + SIXELSTATUS status = SIXEL_FALSE; + unsigned int i; + unsigned int n; + int ret; + tupletable2 colormap; + unsigned int depth; + int result_depth; + + result_depth = sixel_helper_compute_depth(pixelformat); + if (result_depth <= 0) { + *result = NULL; + goto end; + } + + depth = (unsigned int)result_depth; + + ret = computeColorMapFromInput(data, length, depth, + reqcolors, methodForLargest, + methodForRep, qualityMode, + &colormap, origcolors, allocator); + if (ret != 0) { + *result = NULL; + goto end; + } + *ncolors = colormap.size; + quant_trace(stderr, "tupletable size: %d\n", *ncolors); + *result = (unsigned char *)sixel_allocator_malloc(allocator, *ncolors * depth); + for (i = 0; i < *ncolors; i++) { + for (n = 0; n < depth; ++n) { + (*result)[i * depth + n] = colormap.table[i]->tuple[n]; + } + } + + sixel_allocator_free(allocator, colormap.table); + + status = SIXEL_OK; + +end: + return status; +} + + +/* apply color palette into specified pixel buffers */ +SIXELSTATUS +sixel_quant_apply_palette( + sixel_index_t /* out */ *result, + unsigned char /* in */ *data, + int /* in */ width, + int /* in */ height, + int /* in */ depth, + unsigned char /* in */ *palette, + int /* in */ reqcolor, + int /* in */ methodForDiffuse, + int /* in */ foptimize, + int /* in */ foptimize_palette, + int /* in */ complexion, + unsigned short /* in */ *cachetable, + int /* in */ *ncolors, + sixel_allocator_t /* in */ *allocator) +{ + typedef int component_t; + enum { max_depth = 4 }; + enum { max_channel_diff_sq = 255 * 255 }; + SIXELSTATUS status = SIXEL_FALSE; + int pos, n, x, y, sum1, sum2; + int non_weighted_components; + component_t offset; + int color_index; + long long max_complexion; + unsigned short *indextable; + unsigned char new_palette[SIXEL_PALETTE_MAX * 4]; + unsigned short migration_map[SIXEL_PALETTE_MAX]; + float (*f_mask) (int x, int y, int c) = NULL; + void (*f_diffuse)(unsigned char *data, int width, int height, + int x, int y, int depth, int offset); + int (*f_lookup)(unsigned char const * const pixel, + int const depth, + unsigned char const * const palette, + int const reqcolor, + unsigned short * const cachetable, + int const complexion); + + /* check bad reqcolor */ + if (reqcolor < 1) { + status = SIXEL_BAD_ARGUMENT; + sixel_helper_set_additional_message( + "sixel_quant_apply_palette: " + "a bad argument is detected, reqcolor < 0."); + goto end; + } + + /* NOTE: diffuse_jajuni, diffuse_stucki, and diffuse_burkes reference at + * minimum the position pos + width * 1 - 2, so width must be at least 2 + * to avoid underflow. + * On the other hand, diffuse_fs and diffuse_atkinson + * reference pos + width * 1 - 1, but since these functions are only called + * when width >= 1, they do not cause underflow. + */ + if (depth != 3) { + f_diffuse = diffuse_none; + } else { + switch (methodForDiffuse) { + case SIXEL_DIFFUSE_NONE: + f_diffuse = diffuse_none; + break; + case SIXEL_DIFFUSE_ATKINSON: + f_diffuse = diffuse_atkinson; + break; + case SIXEL_DIFFUSE_FS: + f_diffuse = diffuse_fs; + break; + case SIXEL_DIFFUSE_JAJUNI: + /* fallback to diffuse_none if width < 2 */ + f_diffuse = width >= 2 ? diffuse_jajuni: diffuse_none; + break; + case SIXEL_DIFFUSE_STUCKI: + /* fallback to diffuse_none if width < 2 */ + f_diffuse = width >= 2 ? diffuse_stucki: diffuse_none; + break; + case SIXEL_DIFFUSE_BURKES: + /* fallback to diffuse_none if width < 2 */ + f_diffuse = width >= 2 ? diffuse_burkes: diffuse_none; + break; + case SIXEL_DIFFUSE_A_DITHER: + f_diffuse = diffuse_none; + f_mask = mask_a; + break; + case SIXEL_DIFFUSE_X_DITHER: + f_diffuse = diffuse_none; + f_mask = mask_x; + break; + default: + quant_trace(stderr, "Internal error: invalid value of" + " methodForDiffuse: %d\n", + methodForDiffuse); + f_diffuse = diffuse_none; + break; + } + } + + f_lookup = NULL; + if (reqcolor == 2) { + sum1 = 0; + sum2 = 0; + for (n = 0; n < depth; ++n) { + sum1 += palette[n]; + } + for (n = depth; n < depth + depth; ++n) { + sum2 += palette[n]; + } + if (sum1 == 0 && sum2 == 255 * 3) { + f_lookup = lookup_mono_darkbg; + } else if (sum1 == 255 * 3 && sum2 == 0) { + f_lookup = lookup_mono_lightbg; + } + } + if (f_lookup == NULL) { + if (foptimize && depth == 3) { + f_lookup = lookup_fast; + } else { + f_lookup = lookup_normal; + } + } + + if ((f_lookup == lookup_fast || f_lookup == lookup_normal) && complexion > 1) { + non_weighted_components = depth > 1 ? depth - 1 : 0; + max_complexion = (INT_MAX - (long long)max_channel_diff_sq + * (long long)non_weighted_components) + / (long long)max_channel_diff_sq; + if ((long long)complexion > max_complexion) { + status = SIXEL_BAD_ARGUMENT; + sixel_helper_set_additional_message( + "sixel_quant_apply_palette: complexion parameter is too large."); + goto end; + } + } + + indextable = cachetable; + if (cachetable == NULL && f_lookup == lookup_fast) { + indextable = (unsigned short *)sixel_allocator_calloc(allocator, + (size_t)(1 << depth * 5), + sizeof(unsigned short)); + if (!indextable) { + quant_trace(stderr, "Unable to allocate memory for indextable.\n"); + goto end; + } + } + + if (foptimize_palette) { + *ncolors = 0; + + memset(new_palette, 0x00, sizeof(SIXEL_PALETTE_MAX * depth)); + memset(migration_map, 0x00, sizeof(migration_map)); + + if (f_mask) { + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + unsigned char copy[max_depth]; + int d; + int val; + + pos = y * width + x; + for (d = 0; d < depth; d ++) { + val = data[pos * depth + d] + f_mask(x, y, d) * 32; + copy[d] = val < 0 ? 0 : val > 255 ? 255 : val; + } + color_index = f_lookup(copy, depth, + palette, reqcolor, indextable, complexion); + if (migration_map[color_index] == 0) { + result[pos] = *ncolors; + for (n = 0; n < depth; ++n) { + new_palette[*ncolors * depth + n] = palette[color_index * depth + n]; + } + ++*ncolors; + migration_map[color_index] = *ncolors; + } else { + result[pos] = migration_map[color_index] - 1; + } + } + } + memcpy(palette, new_palette, (size_t)(*ncolors * depth)); + } else { + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + pos = y * width + x; + color_index = f_lookup(data + (pos * depth), depth, + palette, reqcolor, indextable, complexion); + if (migration_map[color_index] == 0) { + result[pos] = *ncolors; + for (n = 0; n < depth; ++n) { + new_palette[*ncolors * depth + n] = palette[color_index * depth + n]; + } + ++*ncolors; + migration_map[color_index] = *ncolors; + } else { + result[pos] = migration_map[color_index] - 1; + } + for (n = 0; n < depth; ++n) { + offset = data[pos * depth + n] - palette[color_index * depth + n]; + f_diffuse(data + n, width, height, x, y, depth, offset); + } + } + } + memcpy(palette, new_palette, (size_t)(*ncolors * depth)); + } + } else { + if (f_mask) { + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + unsigned char copy[max_depth]; + int d; + int val; + + pos = y * width + x; + for (d = 0; d < depth; d ++) { + val = data[pos * depth + d] + f_mask(x, y, d) * 32; + copy[d] = val < 0 ? 0 : val > 255 ? 255 : val; + } + result[pos] = f_lookup(copy, depth, + palette, reqcolor, indextable, complexion); + } + } + } else { + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + pos = y * width + x; + color_index = f_lookup(data + (pos * depth), depth, + palette, reqcolor, indextable, complexion); + result[pos] = color_index; + for (n = 0; n < depth; ++n) { + offset = data[pos * depth + n] - palette[color_index * depth + n]; + f_diffuse(data + n, width, height, x, y, depth, offset); + } + } + } + } + *ncolors = reqcolor; + } + + if (cachetable == NULL) { + sixel_allocator_free(allocator, indextable); + } + + status = SIXEL_OK; + +end: + return status; +} + + +void +sixel_quant_free_palette( + unsigned char /* in */ *data, + sixel_allocator_t /* in */ *allocator) +{ + sixel_allocator_free(allocator, data); +} + + +#if HAVE_TESTS +static int +test1(void) +{ + int nret = EXIT_FAILURE; + sample minval[1] = { 1 }; + sample maxval[1] = { 2 }; + unsigned int retval; + + retval = largestByLuminosity(minval, maxval, 1); + if (retval != 0) { + goto error; + } + nret = EXIT_SUCCESS; + +error: + return nret; +} + + +SIXELAPI int +sixel_quant_tests_main(void) +{ + int nret = EXIT_FAILURE; + size_t i; + typedef int (* testcase)(void); + + static testcase const testcases[] = { + test1, + }; + + for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) { + nret = testcases[i](); + if (nret != EXIT_SUCCESS) { + goto error; + } + } + + nret = EXIT_SUCCESS; + +error: + return nret; +} +#endif /* HAVE_TESTS */ + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ diff --git a/src/3rdparty/sixel/quant.h b/src/3rdparty/sixel/quant.h new file mode 100644 index 000000000..5ecf28b47 --- /dev/null +++ b/src/3rdparty/sixel/quant.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2014-2016 Hayaki Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef LIBSIXEL_QUANT_H +#define LIBSIXEL_QUANT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* choose colors using median-cut method */ +SIXELSTATUS +sixel_quant_make_palette( + unsigned char /* out */ **result, + unsigned const char /* in */ *data, /* data for sampling */ + unsigned int /* in */ length, /* data size */ + int /* in */ pixelformat, + unsigned int /* in */ reqcolors, + unsigned int /* in */ *ncolors, + unsigned int /* in */ *origcolors, + int /* in */ methodForLargest, + int /* in */ methodForRep, + int /* in */ qualityMode, + sixel_allocator_t /* in */ *allocator); + + +/* apply color palette into specified pixel buffers */ +SIXELSTATUS +sixel_quant_apply_palette( + sixel_index_t /* out */ *result, + unsigned char /* in */ *data, + int /* in */ width, + int /* in */ height, + int /* in */ pixelformat, + unsigned char /* in */ *palette, + int /* in */ reqcolor, + int const /* in */ methodForDiffuse, + int /* in */ foptimize, + int /* in */ foptimize_palette, + int /* in */ complexion, + unsigned short /* in */ *cachetable, + int /* in */ *ncolor, + sixel_allocator_t /* in */ *allocator); + + +/* deallocate specified palette */ +void +sixel_quant_free_palette( + unsigned char /* in */ *data, + sixel_allocator_t /* in */ *allocator); + +#if HAVE_TESTS +int +sixel_quant_tests_main(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LIBSIXEL_QUANT_H */ + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ diff --git a/src/3rdparty/sixel/repo.json b/src/3rdparty/sixel/repo.json new file mode 100644 index 000000000..481ed232f --- /dev/null +++ b/src/3rdparty/sixel/repo.json @@ -0,0 +1,6 @@ +{ + "home": "https://github.com/saitoha/libsixel", + "license": "MIT ( embed in source )", + "version": "1.8.7-r2", + "author": "Hayaki Saito" +} diff --git a/src/3rdparty/sixel/sixel.h b/src/3rdparty/sixel/sixel.h new file mode 100644 index 000000000..73afc971a --- /dev/null +++ b/src/3rdparty/sixel/sixel.h @@ -0,0 +1,1173 @@ +/* + * Copyright (c) 2014-2020 Hayaki Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include /* for size_t */ + +#ifndef LIBSIXEL_SIXEL_H +#define LIBSIXEL_SIXEL_H + +/* + * fastfetch: upstream defines this as `__declspec(dllexport)` on Windows, but + * this vendored subset is compiled into libfastfetch statically rather than + * into a standalone DLL. Keeping the attribute would (a) add every libsixel + * symbol to fastfetch's export table and (b) make these declarations disagree + * with the ones in dither.h / output.h / quant.h / allocator.h, which do not + * carry it (clang: -Wdll-attribute-on-redeclaration). + * Re-apply this change when re-syncing with upstream. + */ +#define SIXELAPI + +#define LIBSIXEL_VERSION "1.8.7-r2" +#define LIBSIXEL_ABI_VERSION "1:6:0" + +typedef unsigned char sixel_index_t; + +/* limitations */ +#define SIXEL_OUTPUT_PACKET_SIZE 16384 +#define SIXEL_PALETTE_MIN 2 +#define SIXEL_PALETTE_MAX 256 +#define SIXEL_USE_DEPRECATED_SYMBOLS 1 +#define SIXEL_ALLOCATE_BYTES_MAX 10248UL * 1024UL * 128UL /* up to 128M */ +#define SIXEL_WIDTH_LIMIT 1000000 +#define SIXEL_HEIGHT_LIMIT 1000000 + +/* loader settings */ +#define SIXEL_DEFALUT_GIF_DELAY 1 + +/* return value */ +typedef int SIXELSTATUS; +#define SIXEL_OK 0x0000 /* succeeded */ +#define SIXEL_FALSE 0x1000 /* failed */ + +#define SIXEL_RUNTIME_ERROR (SIXEL_FALSE | 0x0100) /* runtime error */ +#define SIXEL_LOGIC_ERROR (SIXEL_FALSE | 0x0200) /* logic error */ +#define SIXEL_FEATURE_ERROR (SIXEL_FALSE | 0x0300) /* feature not enabled */ +#define SIXEL_LIBC_ERROR (SIXEL_FALSE | 0x0400) /* errors caused by curl */ +#define SIXEL_CURL_ERROR (SIXEL_FALSE | 0x0500) /* errors occures in libc functions */ +#define SIXEL_JPEG_ERROR (SIXEL_FALSE | 0x0600) /* errors occures in libjpeg functions */ +#define SIXEL_PNG_ERROR (SIXEL_FALSE | 0x0700) /* errors occures in libpng functions */ +#define SIXEL_GDK_ERROR (SIXEL_FALSE | 0x0800) /* errors occures in gdk functions */ +#define SIXEL_GD_ERROR (SIXEL_FALSE | 0x0900) /* errors occures in gd functions */ +#define SIXEL_STBI_ERROR (SIXEL_FALSE | 0x0a00) /* errors occures in stb_image functions */ +#define SIXEL_STBIW_ERROR (SIXEL_FALSE | 0x0b00) /* errors occures in stb_image_write functions */ + +#define SIXEL_INTERRUPTED (SIXEL_OK | 0x0001) /* interrupted by a signal */ + +#define SIXEL_BAD_ALLOCATION (SIXEL_RUNTIME_ERROR | 0x0001) /* malloc() failed */ +#define SIXEL_BAD_ARGUMENT (SIXEL_RUNTIME_ERROR | 0x0002) /* bad argument detected */ +#define SIXEL_BAD_INPUT (SIXEL_RUNTIME_ERROR | 0x0003) /* bad input detected */ +#define SIXEL_BAD_INTEGER_OVERFLOW (SIXEL_RUNTIME_ERROR | 0x0004) /* integer overflow */ + +#define SIXEL_NOT_IMPLEMENTED (SIXEL_FEATURE_ERROR | 0x0001) /* feature not implemented */ + +#define SIXEL_SUCCEEDED(status) (((status) & 0x1000) == 0) +#define SIXEL_FAILED(status) (((status) & 0x1000) != 0) + +/* method for finding the largest dimension for splitting, + * and sorting by that component */ +#define SIXEL_LARGE_AUTO 0x0 /* choose automatically the method for finding the largest + dimension */ +#define SIXEL_LARGE_NORM 0x1 /* simply comparing the range in RGB space */ +#define SIXEL_LARGE_LUM 0x2 /* transforming into luminosities before the comparison */ + +/* method for choosing a color from the box */ +#define SIXEL_REP_AUTO 0x0 /* choose automatically the method for selecting + representative color from each box */ +#define SIXEL_REP_CENTER_BOX 0x1 /* choose the center of the box */ +#define SIXEL_REP_AVERAGE_COLORS 0x2 /* choose the average all the color + in the box (specified in Heckbert's paper) */ +#define SIXEL_REP_AVERAGE_PIXELS 0x3 /* choose the average all the pixels in the box */ + +/* method for diffusing */ +#define SIXEL_DIFFUSE_AUTO 0x0 /* choose diffusion type automatically */ +#define SIXEL_DIFFUSE_NONE 0x1 /* don't diffuse */ +#define SIXEL_DIFFUSE_ATKINSON 0x2 /* diffuse with Bill Atkinson's method */ +#define SIXEL_DIFFUSE_FS 0x3 /* diffuse with Floyd-Steinberg method */ +#define SIXEL_DIFFUSE_JAJUNI 0x4 /* diffuse with Jarvis, Judice & Ninke method */ +#define SIXEL_DIFFUSE_STUCKI 0x5 /* diffuse with Stucki's method */ +#define SIXEL_DIFFUSE_BURKES 0x6 /* diffuse with Burkes' method */ +#define SIXEL_DIFFUSE_A_DITHER 0x7 /* positionally stable arithmetic dither */ +#define SIXEL_DIFFUSE_X_DITHER 0x8 /* positionally stable arithmetic xor based dither */ + +/* quality modes */ +#define SIXEL_QUALITY_AUTO 0x0 /* choose quality mode automatically */ +#define SIXEL_QUALITY_HIGH 0x1 /* high quality palette construction */ +#define SIXEL_QUALITY_LOW 0x2 /* low quality palette construction */ +#define SIXEL_QUALITY_FULL 0x3 /* full quality palette construction */ +#define SIXEL_QUALITY_HIGHCOLOR 0x4 /* high color */ + +/* built-in dither */ +#define SIXEL_BUILTIN_MONO_DARK 0x0 /* monochrome terminal with dark background */ +#define SIXEL_BUILTIN_MONO_LIGHT 0x1 /* monochrome terminal with light background */ +#define SIXEL_BUILTIN_XTERM16 0x2 /* xterm 16color */ +#define SIXEL_BUILTIN_XTERM256 0x3 /* xterm 256color */ +#define SIXEL_BUILTIN_VT340_MONO 0x4 /* vt340 monochrome */ +#define SIXEL_BUILTIN_VT340_COLOR 0x5 /* vt340 color */ +#define SIXEL_BUILTIN_G1 0x6 /* 1bit grayscale */ +#define SIXEL_BUILTIN_G2 0x7 /* 2bit grayscale */ +#define SIXEL_BUILTIN_G4 0x8 /* 4bit grayscale */ +#define SIXEL_BUILTIN_G8 0x9 /* 8bit grayscale */ + +/* offset value of pixelFormat */ +#define SIXEL_FORMATTYPE_COLOR (0) +#define SIXEL_FORMATTYPE_GRAYSCALE (1 << 6) +#define SIXEL_FORMATTYPE_PALETTE (1 << 7) + +/* pixelformat type of input image + NOTE: for compatibility, the value of PIXELFORAMT_COLOR_RGB888 must be 3 */ +#define SIXEL_PIXELFORMAT_RGB555 (SIXEL_FORMATTYPE_COLOR | 0x01) /* 15bpp */ +#define SIXEL_PIXELFORMAT_RGB565 (SIXEL_FORMATTYPE_COLOR | 0x02) /* 16bpp */ +#define SIXEL_PIXELFORMAT_RGB888 (SIXEL_FORMATTYPE_COLOR | 0x03) /* 24bpp */ +#define SIXEL_PIXELFORMAT_BGR555 (SIXEL_FORMATTYPE_COLOR | 0x04) /* 15bpp */ +#define SIXEL_PIXELFORMAT_BGR565 (SIXEL_FORMATTYPE_COLOR | 0x05) /* 16bpp */ +#define SIXEL_PIXELFORMAT_BGR888 (SIXEL_FORMATTYPE_COLOR | 0x06) /* 24bpp */ +#define SIXEL_PIXELFORMAT_ARGB8888 (SIXEL_FORMATTYPE_COLOR | 0x10) /* 32bpp */ +#define SIXEL_PIXELFORMAT_RGBA8888 (SIXEL_FORMATTYPE_COLOR | 0x11) /* 32bpp */ +#define SIXEL_PIXELFORMAT_ABGR8888 (SIXEL_FORMATTYPE_COLOR | 0x12) /* 32bpp */ +#define SIXEL_PIXELFORMAT_BGRA8888 (SIXEL_FORMATTYPE_COLOR | 0x13) /* 32bpp */ +#define SIXEL_PIXELFORMAT_G1 (SIXEL_FORMATTYPE_GRAYSCALE | 0x00) /* 1bpp grayscale */ +#define SIXEL_PIXELFORMAT_G2 (SIXEL_FORMATTYPE_GRAYSCALE | 0x01) /* 2bpp grayscale */ +#define SIXEL_PIXELFORMAT_G4 (SIXEL_FORMATTYPE_GRAYSCALE | 0x02) /* 4bpp grayscale */ +#define SIXEL_PIXELFORMAT_G8 (SIXEL_FORMATTYPE_GRAYSCALE | 0x03) /* 8bpp grayscale */ +#define SIXEL_PIXELFORMAT_AG88 (SIXEL_FORMATTYPE_GRAYSCALE | 0x13) /* 16bpp gray+alpha */ +#define SIXEL_PIXELFORMAT_GA88 (SIXEL_FORMATTYPE_GRAYSCALE | 0x23) /* 16bpp gray+alpha */ +#define SIXEL_PIXELFORMAT_PAL1 (SIXEL_FORMATTYPE_PALETTE | 0x00) /* 1bpp palette */ +#define SIXEL_PIXELFORMAT_PAL2 (SIXEL_FORMATTYPE_PALETTE | 0x01) /* 2bpp palette */ +#define SIXEL_PIXELFORMAT_PAL4 (SIXEL_FORMATTYPE_PALETTE | 0x02) /* 4bpp palette */ +#define SIXEL_PIXELFORMAT_PAL8 (SIXEL_FORMATTYPE_PALETTE | 0x03) /* 8bpp palette */ + +/* palette type */ +#define SIXEL_PALETTETYPE_AUTO 0 /* choose palette type automatically */ +#define SIXEL_PALETTETYPE_HLS 1 /* HLS colorspace */ +#define SIXEL_PALETTETYPE_RGB 2 /* RGB colorspace */ + +/* policies of SIXEL encoding */ +#define SIXEL_ENCODEPOLICY_AUTO 0 /* choose encoding policy automatically */ +#define SIXEL_ENCODEPOLICY_FAST 1 /* encode as fast as possible */ +#define SIXEL_ENCODEPOLICY_SIZE 2 /* encode to as small sixel sequence as possible */ + +/* method for re-sampling */ +#define SIXEL_RES_NEAREST 0 /* Use nearest neighbor method */ +#define SIXEL_RES_GAUSSIAN 1 /* Use guaussian filter */ +#define SIXEL_RES_HANNING 2 /* Use hanning filter */ +#define SIXEL_RES_HAMMING 3 /* Use hamming filter */ +#define SIXEL_RES_BILINEAR 4 /* Use bilinear filter */ +#define SIXEL_RES_WELSH 5 /* Use welsh filter */ +#define SIXEL_RES_BICUBIC 6 /* Use bicubic filter */ +#define SIXEL_RES_LANCZOS2 7 /* Use lanczos-2 filter */ +#define SIXEL_RES_LANCZOS3 8 /* Use lanczos-3 filter */ +#define SIXEL_RES_LANCZOS4 9 /* Use lanczos-4 filter */ + +/* image format */ +#define SIXEL_FORMAT_GIF 0x0 /* read only */ +#define SIXEL_FORMAT_PNG 0x1 /* read/write */ +#define SIXEL_FORMAT_BMP 0x2 /* read only */ +#define SIXEL_FORMAT_JPG 0x3 /* read only */ +#define SIXEL_FORMAT_TGA 0x4 /* read only */ +#define SIXEL_FORMAT_WBMP 0x5 /* read only with --with-gd configure option */ +#define SIXEL_FORMAT_TIFF 0x6 /* read only */ +#define SIXEL_FORMAT_SIXEL 0x7 /* read only */ +#define SIXEL_FORMAT_PNM 0x8 /* read only */ +#define SIXEL_FORMAT_GD2 0x9 /* read only with --with-gd configure option */ +#define SIXEL_FORMAT_PSD 0xa /* read only */ +#define SIXEL_FORMAT_HDR 0xb /* read only */ + +/* loop mode */ +#define SIXEL_LOOP_AUTO 0 /* honer the setting of GIF header */ +#define SIXEL_LOOP_FORCE 1 /* always enable loop */ +#define SIXEL_LOOP_DISABLE 2 /* always disable loop */ + +/* setopt flags */ +#define SIXEL_OPTFLAG_INPUT ('i') /* -i, --input: specify input file name. */ +#define SIXEL_OPTFLAG_OUTPUT ('o') /* -o, --output: specify output file name. */ +#define SIXEL_OPTFLAG_OUTFILE ('o') /* -o, --outfile: specify output file name. */ +#define SIXEL_OPTFLAG_7BIT_MODE ('7') /* -7, --7bit-mode: for 7bit terminals or printers (default) */ +#define SIXEL_OPTFLAG_8BIT_MODE ('8') /* -8, --8bit-mode: for 8bit terminals or printers */ +#define SIXEL_OPTFLAG_HAS_GRI_ARG_LIMIT ('R') /* -R, --gri-limit: limit arguments of DECGRI('!') to 255 */ +#define SIXEL_OPTFLAG_COLORS ('p') /* -p COLORS, --colors=COLORS: specify number of colors */ +#define SIXEL_OPTFLAG_MAPFILE ('m') /* -m FILE, --mapfile=FILE: specify set of colors */ +#define SIXEL_OPTFLAG_MONOCHROME ('e') /* -e, --monochrome: output monochrome sixel image */ +#define SIXEL_OPTFLAG_INSECURE ('k') /* -k, --insecure: allow to connect to SSL sites without certs */ +#define SIXEL_OPTFLAG_INVERT ('i') /* -i, --invert: assume the terminal background color */ +#define SIXEL_OPTFLAG_HIGH_COLOR ('I') /* -I, --high-color: output 15bpp sixel image */ +#define SIXEL_OPTFLAG_USE_MACRO ('u') /* -u, --use-macro: use DECDMAC and DEVINVM sequences */ +#define SIXEL_OPTFLAG_MACRO_NUMBER ('n') /* -n MACRONO, --macro-number=MACRONO: + specify macro register number */ +#define SIXEL_OPTFLAG_COMPLEXION_SCORE ('C') /* -C COMPLEXIONSCORE, --complexion-score=COMPLEXIONSCORE: + specify an number argument for the score of + complexion correction. */ +#define SIXEL_OPTFLAG_IGNORE_DELAY ('g') /* -g, --ignore-delay: render GIF animation without delay */ +#define SIXEL_OPTFLAG_STATIC ('S') /* -S, --static: render animated GIF as a static image */ +#define SIXEL_OPTFLAG_DIFFUSION ('d') /* -d DIFFUSIONTYPE, --diffusion=DIFFUSIONTYPE: + choose diffusion method which used with -p option. + DIFFUSIONTYPE is one of them: + auto -> choose diffusion type + automatically (default) + none -> do not diffuse + fs -> Floyd-Steinberg method + atkinson -> Bill Atkinson's method + jajuni -> Jarvis, Judice & Ninke + stucki -> Stucki's method + burkes -> Burkes' method + a_dither -> positionally stable + arithmetic dither + a_dither -> positionally stable + arithmetic xor based dither + */ +#define SIXEL_OPTFLAG_FIND_LARGEST ('f') /* -f FINDTYPE, --find-largest=FINDTYPE: + choose method for finding the largest + dimension of median cut boxes for + splitting, make sense only when -p + option (color reduction) is + specified + FINDTYPE is one of them: + auto -> choose finding method + automatically (default) + norm -> simply comparing the + range in RGB space + lum -> transforming into + luminosities before the + comparison + */ +#define SIXEL_OPTFLAG_SELECT_COLOR ('s') /* -s SELECTTYPE, --select-color=SELECTTYPE + choose the method for selecting + representative color from each + median-cut box, make sense only + when -p option (color reduction) is + specified + SELECTTYPE is one of them: + auto -> choose selecting + method automatically + (default) + center -> choose the center of + the box + average -> calculate the color + average into the box + histogram -> similar with average + but considers color + histogram + */ +#define SIXEL_OPTFLAG_CROP ('c') /* -c REGION, --crop=REGION: + crop source image to fit the + specified geometry. REGION should + be formatted as '%dx%d+%d+%d' + */ +#define SIXEL_OPTFLAG_WIDTH ('w') /* -w WIDTH, --width=WIDTH: + resize image to specified width + WIDTH is represented by the + following syntax + auto -> preserving aspect + ratio (default) + % -> scale width with + given percentage + -> scale width with + pixel counts + px -> scale width with + pixel counts + */ +#define SIXEL_OPTFLAG_HEIGHT ('h') /* -h HEIGHT, --height=HEIGHT: + resize image to specified height + HEIGHT is represented by the + following syntax + auto -> preserving aspect + ratio (default) + % -> scale height with + given percentage + -> scale height with + pixel counts + px -> scale height with + pixel counts + */ +#define SIXEL_OPTFLAG_RESAMPLING ('r') /* -r RESAMPLINGTYPE, --resampling=RESAMPLINGTYPE: + choose resampling filter used + with -w or -h option (scaling) + RESAMPLINGTYPE is one of them: + nearest -> Nearest-Neighbor + method + gaussian -> Gaussian filter + hanning -> Hanning filter + hamming -> Hamming filter + bilinear -> Bilinear filter + (default) + welsh -> Welsh filter + bicubic -> Bicubic filter + lanczos2 -> Lanczos-2 filter + lanczos3 -> Lanczos-3 filter + lanczos4 -> Lanczos-4 filter + */ +#define SIXEL_OPTFLAG_QUALITY ('q') /* -q QUALITYMODE, --quality=QUALITYMODE: + select quality of color + quanlization. + auto -> decide quality mode + automatically (default) + low -> low quality and high + speed mode + high -> high quality and low + speed mode + full -> full quality and careful + speed mode + */ +#define SIXEL_OPTFLAG_LOOPMODE ('l') /* -l LOOPMODE, --loop-control=LOOPMODE: + select loop control mode for GIF + animation. + auto -> honor the setting of + GIF header (default) + force -> always enable loop + disable -> always disable loop + */ +#define SIXEL_OPTFLAG_PALETTE_TYPE ('t') /* -t PALETTETYPE, --palette-type=PALETTETYPE: + select palette color space type + auto -> choose palette type + automatically (default) + hls -> use HLS color space + rgb -> use RGB color space + */ +#define SIXEL_OPTFLAG_BUILTIN_PALETTE ('b') /* -b BUILTINPALETTE, --builtin-palette=BUILTINPALETTE: + select built-in palette type + xterm16 -> X default 16 color map + xterm256 -> X default 256 color map + vt340mono -> VT340 monochrome map + vt340color -> VT340 color map + gray1 -> 1bit grayscale map + gray2 -> 2bit grayscale map + gray4 -> 4bit grayscale map + gray8 -> 8bit grayscale map + */ +#define SIXEL_OPTFLAG_ENCODE_POLICY ('E') /* -E ENCODEPOLICY, --encode-policy=ENCODEPOLICY: + select encoding policy + auto -> choose encoding policy + automatically (default) + fast -> encode as fast as possible + size -> encode to as small sixel + sequence as possible + */ +#define SIXEL_OPTFLAG_BGCOLOR ('B') /* -B BGCOLOR, --bgcolor=BGCOLOR: + specify background color + BGCOLOR is represented by the + following syntax + #rgb + #rrggbb + #rrrgggbbb + #rrrrggggbbbb + rgb:r/g/b + rgb:rr/gg/bb + rgb:rrr/ggg/bbb + rgb:rrrr/gggg/bbbb + */ +#define SIXEL_OPTFLAG_PENETRATE ('P') /* -P, --penetrate: + penetrate GNU Screen using DCS + pass-through sequence */ +#define SIXEL_OPTFLAG_PIPE_MODE ('D') /* -D, --pipe-mode: (deprecated) + read source images from stdin continuously */ +#define SIXEL_OPTFLAG_VERBOSE ('v') /* -v, --verbose: show debugging info */ +#define SIXEL_OPTFLAG_VERSION ('V') /* -V, --version: show version and license info */ +#define SIXEL_OPTFLAG_HELP ('H') /* -H, --help: show this help */ + +#if SIXEL_USE_DEPRECATED_SYMBOLS +/* output character size */ +enum characterSize { + CSIZE_7BIT = 0, /* 7bit character */ + CSIZE_8BIT = 1 /* 8bit character */ +}; + +/* method for finding the largest dimension for splitting, + * and sorting by that component */ +enum methodForLargest { + LARGE_AUTO = 0, /* choose automatically the method for finding the largest + dimension */ + LARGE_NORM = 1, /* simply comparing the range in RGB space */ + LARGE_LUM = 2 /* transforming into luminosities before the comparison */ +}; + +/* method for choosing a color from the box */ +enum methodForRep { + REP_AUTO = 0, /* choose automatically the method for selecting + representative color from each box */ + REP_CENTER_BOX = 1, /* choose the center of the box */ + REP_AVERAGE_COLORS = 2, /* choose the average all the color + in the box (specified in Heckbert's paper) */ + REP_AVERAGE_PIXELS = 3 /* choose the average all the pixels in the box */ +}; + +/* method for diffusing */ +enum methodForDiffuse { + DIFFUSE_AUTO = 0, /* choose diffusion type automatically */ + DIFFUSE_NONE = 1, /* don't diffuse */ + DIFFUSE_ATKINSON = 2, /* diffuse with Bill Atkinson's method */ + DIFFUSE_FS = 3, /* diffuse with Floyd-Steinberg method */ + DIFFUSE_JAJUNI = 4, /* diffuse with Jarvis, Judice & Ninke method */ + DIFFUSE_STUCKI = 5, /* diffuse with Stucki's method */ + DIFFUSE_BURKES = 6, /* diffuse with Burkes' method */ + DIFFUSE_A_DITHER = 7, /* positionally stable arithmetic dither */ + DIFFUSE_X_DITHER = 8 /* positionally stable arithmetic xor based dither */ +}; + +/* quality modes */ +enum qualityMode { + QUALITY_AUTO = 0, /* choose quality mode automatically */ + QUALITY_HIGH = 1, /* high quality palette construction */ + QUALITY_LOW = 2, /* low quality palette construction */ + QUALITY_FULL = 3, /* full quality palette construction */ + QUALITY_HIGHCOLOR = 4 /* high color */ +}; + +/* built-in dither */ +enum builtinDither { + BUILTIN_MONO_DARK = 0, /* monochrome terminal with dark background */ + BUILTIN_MONO_LIGHT = 1, /* monochrome terminal with dark background */ + BUILTIN_XTERM16 = 2, /* xterm 16color */ + BUILTIN_XTERM256 = 3, /* xterm 256color */ + BUILTIN_VT340_MONO = 4, /* vt340 monochrome */ + BUILTIN_VT340_COLOR = 5 /* vt340 color */ +}; + +/* offset value of enum pixelFormat */ +enum formatType { + FORMATTYPE_COLOR = 0, + FORMATTYPE_GRAYSCALE = 1 << 6, + FORMATTYPE_PALETTE = 1 << 7 +}; + +/* pixelformat type of input image + NOTE: for compatibility, the value of PIXELFORAMT_COLOR_RGB888 must be 3 */ +enum pixelFormat { + PIXELFORMAT_RGB555 = FORMATTYPE_COLOR | 0x01, /* 15bpp */ + PIXELFORMAT_RGB565 = FORMATTYPE_COLOR | 0x02, /* 16bpp */ + PIXELFORMAT_RGB888 = FORMATTYPE_COLOR | 0x03, /* 24bpp */ + PIXELFORMAT_BGR555 = FORMATTYPE_COLOR | 0x04, /* 15bpp */ + PIXELFORMAT_BGR565 = FORMATTYPE_COLOR | 0x05, /* 16bpp */ + PIXELFORMAT_BGR888 = FORMATTYPE_COLOR | 0x06, /* 24bpp */ + PIXELFORMAT_ARGB8888 = FORMATTYPE_COLOR | 0x10, /* 32bpp */ + PIXELFORMAT_RGBA8888 = FORMATTYPE_COLOR | 0x11, /* 32bpp */ + PIXELFORMAT_G1 = FORMATTYPE_GRAYSCALE | 0x00, /* 1bpp grayscale */ + PIXELFORMAT_G2 = FORMATTYPE_GRAYSCALE | 0x01, /* 2bpp grayscale */ + PIXELFORMAT_G4 = FORMATTYPE_GRAYSCALE | 0x02, /* 4bpp grayscale */ + PIXELFORMAT_G8 = FORMATTYPE_GRAYSCALE | 0x03, /* 8bpp grayscale */ + PIXELFORMAT_AG88 = FORMATTYPE_GRAYSCALE | 0x13, /* 16bpp gray+alpha */ + PIXELFORMAT_GA88 = FORMATTYPE_GRAYSCALE | 0x23, /* 16bpp gray+alpha */ + PIXELFORMAT_PAL1 = FORMATTYPE_PALETTE | 0x00, /* 1bpp palette */ + PIXELFORMAT_PAL2 = FORMATTYPE_PALETTE | 0x01, /* 2bpp palette */ + PIXELFORMAT_PAL4 = FORMATTYPE_PALETTE | 0x02, /* 4bpp palette */ + PIXELFORMAT_PAL8 = FORMATTYPE_PALETTE | 0x03 /* 8bpp palette */ +}; + +/* palette type */ +enum paletteType { + PALETTETYPE_AUTO = 0, /* choose palette type automatically */ + PALETTETYPE_HLS = 1, /* HLS colorspace */ + PALETTETYPE_RGB = 2 /* RGB colorspace */ +}; + +/* policies of SIXEL encoding */ +enum encodePolicy { + ENCODEPOLICY_AUTO = 0, /* choose encoding policy automatically */ + ENCODEPOLICY_FAST = 1, /* encode as fast as possible */ + ENCODEPOLICY_SIZE = 2 /* encode to as small sixel sequence as possible */ +}; + +/* method for re-sampling */ +enum methodForResampling { + RES_NEAREST = 0, /* Use nearest neighbor method */ + RES_GAUSSIAN = 1, /* Use guaussian filter */ + RES_HANNING = 2, /* Use hanning filter */ + RES_HAMMING = 3, /* Use hamming filter */ + RES_BILINEAR = 4, /* Use bilinear filter */ + RES_WELSH = 5, /* Use welsh filter */ + RES_BICUBIC = 6, /* Use bicubic filter */ + RES_LANCZOS2 = 7, /* Use lanczos-2 filter */ + RES_LANCZOS3 = 8, /* Use lanczos-3 filter */ + RES_LANCZOS4 = 9 /* Use lanczos-4 filter */ +}; +#endif + +typedef void *(* sixel_malloc_t)(size_t); +typedef void *(* sixel_calloc_t)(size_t, size_t); +typedef void *(* sixel_realloc_t)(void *, size_t); +typedef void (* sixel_free_t)(void *); + +struct sixel_allocator; +typedef struct sixel_allocator sixel_allocator_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/* create allocator object */ +SIXELAPI SIXELSTATUS +sixel_allocator_new( + sixel_allocator_t /* out */ **ppallocator, /* allocator object to be created */ + sixel_malloc_t /* in */ fn_malloc, /* custom malloc() function */ + sixel_calloc_t /* in */ fn_calloc, /* custom calloc() function */ + sixel_realloc_t /* in */ fn_realloc, /* custom realloc() function */ + sixel_free_t /* in */ fn_free); /* custom free() function */ + +/* increase reference count of allocator object (thread-unsafe) */ +SIXELAPI void +sixel_allocator_ref( + sixel_allocator_t /* in */ *allocator); /* allocator object to be + increment reference counter */ + +/* decrease reference count of output context object (thread-unsafe) */ +SIXELAPI void +sixel_allocator_unref(sixel_allocator_t *allocator); + +/* call custom malloc() */ +SIXELAPI void * +sixel_allocator_malloc( + sixel_allocator_t /* in */ *allocator, /* allocator object */ + size_t /* in */ n); /* allocation size */ + +/* call custom calloc() */ +SIXELAPI void * +sixel_allocator_calloc( + sixel_allocator_t /* in */ *allocator, /* allocator object */ + size_t /* in */ nelm, /* allocation size */ + size_t /* in */ elsize); /* allocation size */ + +/* call custom realloc() */ +SIXELAPI void * +sixel_allocator_realloc( + sixel_allocator_t /* in */ *allocator, /* allocator object */ + void /* in */ *p, /* existing buffer to be re-allocated */ + size_t /* in */ n); /* re-allocation size */ + +/* call custom free() */ +SIXELAPI void +sixel_allocator_free( + sixel_allocator_t /* in */ *allocator, /* allocator object */ + void /* in */ *p); /* existing buffer to be freed */ + +#ifdef HAVE_TESTS +extern volatile int sixel_debug_malloc_counter; + +void * +sixel_bad_malloc(size_t size); + +void * +sixel_bad_calloc(size_t count, size_t size); + +void * +sixel_bad_realloc(void *ptr, size_t size); +#endif /* HAVE_TESTS */ + +#ifdef __cplusplus +} +#endif + +/* output context manipulation API */ + +struct sixel_output; +typedef struct sixel_output sixel_output_t; +typedef int (* sixel_write_function)(char *data, int size, void *priv); + +#ifdef __cplusplus +extern "C" { +#endif + +/* create new output context object */ +SIXELAPI SIXELSTATUS +sixel_output_new( + sixel_output_t /* out */ **output, /* output object to be created */ + sixel_write_function /* in */ fn_write, /* callback for output sixel */ + void /* in */ *priv, /* private data given as + 3rd argument of fn_write */ + sixel_allocator_t /* in */ *allocator); /* allocator, null if you use + default allocator */ + +/* deprecated: create an output object */ +SIXELAPI __attribute__((deprecated)) sixel_output_t * +sixel_output_create( + sixel_write_function /* in */ fn_write, /* callback for output sixel */ + void /* in */ *priv); /* private data given as + 3rd argument of fn_write */ +/* destroy output context object */ +SIXELAPI void +sixel_output_destroy(sixel_output_t /* in */ *output); /* output context */ + +/* increase reference count of output context object (thread-unsafe) */ +SIXELAPI void +sixel_output_ref(sixel_output_t /* in */ *output); /* output context */ + +/* decrease reference count of output context object (thread-unsafe) */ +SIXELAPI void +sixel_output_unref(sixel_output_t /* in */ *output); /* output context */ + +/* get 8bit output mode which indicates whether it uses C1 control characters */ +SIXELAPI int +sixel_output_get_8bit_availability( + sixel_output_t /* in */ *output); /* output context */ + +/* set 8bit output mode state */ +SIXELAPI void +sixel_output_set_8bit_availability( + sixel_output_t /* in */ *output, /* output context */ + int /* in */ availability); /* 0: do not use 8bit characters + 1: use 8bit characters */ + +/* set whether limit arguments of DECGRI('!') to 255 */ +SIXELAPI void +sixel_output_set_gri_arg_limit( + sixel_output_t /* in */ *output, /* output context */ + int /* in */ value); /* 0: don't limit arguments of DECGRI + 1: limit arguments of DECGRI to 255 */ + +/* set GNU Screen penetration feature enable or disable */ +SIXELAPI void +sixel_output_set_penetrate_multiplexer( + sixel_output_t /* in */ *output, /* output context */ + int /* in */ penetrate); /* 0: penetrate GNU Screen + 1: do not penetrate GNU Screen */ + +/* set whether we skip DCS envelope */ +SIXELAPI void +sixel_output_set_skip_dcs_envelope( + sixel_output_t /* in */ *output, /* output context */ + int /* in */ skip); /* 0: output DCS envelope + 1: do not output DCS envelope */ + +/* set palette type: RGB or HLS */ +SIXELAPI void +sixel_output_set_palette_type( + sixel_output_t /* in */ *output, /* output context */ + int /* in */ palettetype); /* PALETTETYPE_RGB: RGB palette + PALETTETYPE_HLS: HLS palette */ + +/* set encodeing policy: auto, fast or size */ +SIXELAPI void +sixel_output_set_encode_policy( + sixel_output_t /* in */ *output, /* output context */ + int /* in */ encode_policy); + + +#ifdef __cplusplus +} +#endif + + +/* color quantization API */ + +/* handle type of dither context object */ +struct sixel_dither; +typedef struct sixel_dither sixel_dither_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/* create dither context object */ +SIXELAPI SIXELSTATUS +sixel_dither_new( + sixel_dither_t /* out */ **ppdither, /* dither object to be created */ + int /* in */ ncolors, /* required colors */ + sixel_allocator_t /* in */ *allocator); /* allocator, null if you use + default allocator */ + +/* create dither context object */ +SIXELAPI __attribute__((deprecated)) sixel_dither_t * +sixel_dither_create(int /* in */ ncolors); /* number of colors */ + +/* get built-in dither context object */ +SIXELAPI sixel_dither_t * +sixel_dither_get(int builtin_dither); /* ID of built-in dither object */ + +/* destroy dither context object */ +SIXELAPI void +sixel_dither_destroy(sixel_dither_t *dither); /* dither context object */ + +/* increase reference count of dither context object (thread-unsafe) */ +SIXELAPI void +sixel_dither_ref(sixel_dither_t *dither); /* dither context object */ + +/* decrease reference count of dither context object (thread-unsafe) */ +SIXELAPI void +sixel_dither_unref(sixel_dither_t *dither); /* dither context object */ + +/* initialize internal palette from specified pixel buffer */ +SIXELAPI SIXELSTATUS +sixel_dither_initialize( + sixel_dither_t *dither, /* dither context object */ + unsigned char /* in */ *data, /* sample image */ + int /* in */ width, /* image width */ + int /* in */ height, /* image height */ + int /* in */ pixelformat, /* one of enum pixelFormat */ + int /* in */ method_for_largest, /* method for finding the largest dimension */ + int /* in */ method_for_rep, /* method for choosing a color from the box */ + int /* in */ quality_mode); /* quality of histogram processing */ + +/* set diffusion type, choose from enum methodForDiffuse */ +SIXELAPI void +sixel_dither_set_diffusion_type( + sixel_dither_t /* in */ *dither, /* dither context object */ + int /* in */ method_for_diffuse); /* one of enum methodForDiffuse */ + +/* get number of palette colors */ +SIXELAPI int +sixel_dither_get_num_of_palette_colors( + sixel_dither_t /* in */ *dither); /* dither context object */ + +/* get number of histogram colors */ +SIXELAPI int +sixel_dither_get_num_of_histogram_colors( + sixel_dither_t /* in */ *dither); /* dither context object */ + +SIXELAPI __attribute__((deprecated)) int /* typoed! remains for compatibility. */ +sixel_dither_get_num_of_histgram_colors( + sixel_dither_t /* in */ *dither); /* dither context object */ + +/* get palette */ +SIXELAPI unsigned char * +sixel_dither_get_palette( + sixel_dither_t /* in */ *dither); /* dither context object */ + +/* set palette */ +SIXELAPI void +sixel_dither_set_palette( + sixel_dither_t /* in */ *dither, /* dither context object */ + unsigned char /* in */ *palette); + +/* set the factor of complexion color correcting */ +SIXELAPI void +sixel_dither_set_complexion_score( + sixel_dither_t /* in */ *dither, /* dither context object */ + int /* in */ score); /* complexion score (>= 1) */ + +/* set whether omitting palette difinition */ +SIXELAPI void +sixel_dither_set_body_only( + sixel_dither_t /* in */ *dither, /* dither context object */ + int /* in */ bodyonly); /* 0: output palette section(default) + 1: do not output palette section */ +/* set whether optimize palette size */ +SIXELAPI void +sixel_dither_set_optimize_palette( + sixel_dither_t /* in */ *dither, /* dither context object */ + int /* in */ do_opt); /* 0: optimize palette size + 1: don't optimize palette size */ +/* set pixelformat */ +SIXELAPI void +sixel_dither_set_pixelformat( + sixel_dither_t /* in */ *dither, /* dither context object */ + int /* in */ pixelformat); /* one of enum pixelFormat */ + +/* set transparent */ +SIXELAPI void +sixel_dither_set_transparent( + sixel_dither_t /* in */ *dither, /* dither context object */ + int /* in */ transparent); /* transparent color index */ + +#ifdef __cplusplus +} +#endif + +/* converter API */ + +typedef void * (* sixel_allocator_function)(size_t size); + +#ifdef __cplusplus +extern "C" { +#endif + +/* convert pixels into sixel format and write it to output context */ +SIXELAPI SIXELSTATUS +sixel_encode( + unsigned char /* in */ *pixels, /* pixel bytes */ + int /* in */ width, /* image width */ + int /* in */ height, /* image height */ + int /* in */ depth, /* color depth: now unused */ + sixel_dither_t /* in */ *dither, /* dither context */ + sixel_output_t /* in */ *context); /* output context */ + +/* convert sixel data into indexed pixel bytes and palette data */ +SIXELAPI SIXELSTATUS +sixel_decode_raw( + unsigned char /* in */ *p, /* sixel bytes */ + int /* in */ len, /* size of sixel bytes */ + unsigned char /* out */ **pixels, /* decoded pixels */ + int /* out */ *pwidth, /* image width */ + int /* out */ *pheight, /* image height */ + unsigned char /* out */ **palette, /* ARGB palette */ + int /* out */ *ncolors, /* palette size (<= 256) */ + sixel_allocator_t /* in */ *allocator); /* allocator object or null */ + +SIXELAPI __attribute__((deprecated)) SIXELSTATUS +sixel_decode( + unsigned char /* in */ *sixels, /* sixel bytes */ + int /* in */ size, /* size of sixel bytes */ + unsigned char /* out */ **pixels, /* decoded pixels */ + int /* out */ *pwidth, /* image width */ + int /* out */ *pheight, /* image height */ + unsigned char /* out */ **palette, /* RGBA palette */ + int /* out */ *ncolors, /* palette size (<= 256) */ + sixel_allocator_function /* in */ fn_malloc); /* malloc function */ + +#ifdef __cplusplus +} +#endif + +/* helper API */ + +#ifdef __cplusplus +extern "C" { +#endif + +SIXELAPI void +sixel_helper_set_additional_message( + const char /* in */ *message /* error message */ +); + +SIXELAPI char const * +sixel_helper_get_additional_message(void); + +/* convert error status code int formatted string */ +SIXELAPI char const * +sixel_helper_format_error( + SIXELSTATUS /* in */ status /* status code */ +); + +/* compute pixel depth from pixelformat */ +SIXELAPI int +sixel_helper_compute_depth( + int /* in */ pixelformat /* one of enum pixelFormat */ +); + +/* convert pixelFormat into PIXELFORMAT_RGB888 */ +SIXELAPI SIXELSTATUS +sixel_helper_normalize_pixelformat( + unsigned char /* out */ *dst, /* destination buffer */ + int /* out */ *dst_pixelformat, /* converted pixelformat */ + unsigned char const /* in */ *src, /* source pixels */ + int /* in */ src_pixelformat, /* format of source image */ + int /* in */ width, /* width of source image */ + int /* in */ height /* height of source image */ +); + +/* scale image to specified size */ +SIXELAPI SIXELSTATUS +sixel_helper_scale_image( + unsigned char /* out */ *dst, /* destination buffer */ + unsigned char const /* in */ *src, /* source image data */ + int /* in */ srcw, /* source image width */ + int /* in */ srch, /* source image height */ + int /* in */ pixelformat, /* one of enum pixelFormat */ + int /* in */ dstw, /* destination image width */ + int /* in */ dsth, /* destination image height */ + int /* in */ method_for_resampling, /* one of methodForResampling */ + sixel_allocator_t /* in */ *allocator /* allocator object */ +); + +#ifdef __cplusplus +} +#endif + + +/* image loader/writer API */ + +#if SIXEL_USE_DEPRECATED_SYMBOLS +enum imageFormat { + FORMAT_GIF = 0, /* read only */ + FORMAT_PNG = 1, /* read/write */ + FORMAT_BMP = 2, /* read only */ + FORMAT_JPG = 3, /* read only */ + FORMAT_TGA = 4, /* read only */ + FORMAT_WBMP = 5, /* read only with --with-gd configure option */ + FORMAT_TIFF = 6, /* read only */ + FORMAT_SIXEL = 7, /* read only */ + FORMAT_PNM = 8, /* read only */ + FORMAT_GD2 = 9, /* read only with --with-gd configure option */ + FORMAT_PSD = 10, /* read only */ + FORMAT_HDR = 11 /* read only */ +}; + +/* loop mode */ +enum loopControl { + LOOP_AUTO = 0, /* honer the setting of GIF header */ + LOOP_FORCE = 1, /* always enable loop */ + LOOP_DISABLE = 2 /* always disable loop */ +}; +#endif + +/* handle type of dither context object */ +struct sixel_frame; +typedef struct sixel_frame sixel_frame_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/* constructor of frame object */ +SIXELAPI SIXELSTATUS +sixel_frame_new( + sixel_frame_t /* out */ **ppframe, /* frame object to be created */ + sixel_allocator_t /* in */ *allocator); /* allocator, null if you use + default allocator */ +/* deprecated version of sixel_frame_new() */ +SIXELAPI __attribute__((deprecated)) sixel_frame_t * +sixel_frame_create(void); + +/* increase reference count of frame object (thread-unsafe) */ +SIXELAPI void +sixel_frame_ref(sixel_frame_t /* in */ *frame); + +/* decrease reference count of frame object (thread-unsafe) */ +SIXELAPI void +sixel_frame_unref(sixel_frame_t /* in */ *frame); + +/* initialize frame object with a pixel buffer */ +SIXELAPI SIXELSTATUS +sixel_frame_init( + sixel_frame_t /* in */ *frame, /* frame object to be initialize */ + unsigned char /* in */ *pixels, /* pixel buffer */ + int /* in */ width, /* pixel width of buffer */ + int /* in */ height, /* pixel height of buffer */ + int /* in */ pixelformat, /* pixelformat of buffer */ + unsigned char /* in */ *palette, /* palette for buffer or NULL */ + int /* in */ ncolors /* number of palette colors or (-1) */ +); + +/* get pixels */ +SIXELAPI unsigned char * +sixel_frame_get_pixels(sixel_frame_t /* in */ *frame); /* frame object */ + +/* get palette */ +SIXELAPI unsigned char * +sixel_frame_get_palette(sixel_frame_t /* in */ *frame); /* frame object */ + +/* get width */ +SIXELAPI int +sixel_frame_get_width(sixel_frame_t /* in */ *frame); /* frame object */ + +/* get height */ +SIXELAPI int +sixel_frame_get_height(sixel_frame_t /* in */ *frame); /* frame object */ + +/* get ncolors */ +SIXELAPI int +sixel_frame_get_ncolors(sixel_frame_t /* in */ *frame); /* frame object */ + +/* get pixelformat */ +SIXELAPI int +sixel_frame_get_pixelformat(sixel_frame_t /* in */ *frame); /* frame object */ + +/* get transparent */ +SIXELAPI int +sixel_frame_get_transparent(sixel_frame_t /* in */ *frame); /* frame object */ + +/* get transparent */ +SIXELAPI int +sixel_frame_get_multiframe(sixel_frame_t /* in */ *frame); /* frame object */ + +/* get delay */ +SIXELAPI int +sixel_frame_get_delay(sixel_frame_t /* in */ *frame); /* frame object */ + +/* get frame no */ +SIXELAPI int +sixel_frame_get_frame_no(sixel_frame_t /* in */ *frame); /* frame object */ + +/* get loop no */ +SIXELAPI int +sixel_frame_get_loop_no(sixel_frame_t /* in */ *frame); /* frame object */ + +/* strip alpha from RGBA/ARGB formatted pixbuf */ +SIXELAPI int +sixel_frame_strip_alpha( + sixel_frame_t /* in */ *frame, + unsigned char /* in */ *bgcolor); + +/* resize a frame to given size with specified resampling filter */ +SIXELAPI SIXELSTATUS +sixel_frame_resize( + sixel_frame_t /* in */ *frame, + int /* in */ width, + int /* in */ height, + int /* in */ method_for_resampling); + +/* clip frame */ +SIXELAPI SIXELSTATUS +sixel_frame_clip( + sixel_frame_t /* in */ *frame, + int /* in */ x, + int /* in */ y, + int /* in */ width, + int /* in */ height); + +typedef SIXELSTATUS (* sixel_load_image_function)( + sixel_frame_t /* in */ *frame, + void /* in/out */ *context); + +/* Note: this function returns SIXEL_OK without calling FN_LOAD when the file + content is empty or 1-byte LF. This implies an assumption that CONTEXT is + initialized to be the default value for the empty file before calling this + function. If it is not the case, the caller needs to properly detect it and + handle this, or otherwise, CONTEXT can be used uninitialized in subsequent + codes. */ +SIXELAPI SIXELSTATUS +sixel_helper_load_image_file( + char const /* in */ *filename, /* source file name */ + int /* in */ fstatic, /* whether to extract static image */ + int /* in */ fuse_palette, /* whether to use paletted image */ + int /* in */ reqcolors, /* requested number of colors */ + unsigned char /* in */ *bgcolor, /* background color */ + int /* in */ loop_control, /* one of enum loopControl */ + sixel_load_image_function /* in */ fn_load, /* callback */ + int /* in */ finsecure, /* true if do not verify SSL */ + int const /* in */ *cancel_flag, /* cancel flag */ + void /* in/out */ *context, /* private data for callback */ + sixel_allocator_t /* in */ *allocator); /* allocator object */ + +/* write image to file */ +SIXELAPI SIXELSTATUS +sixel_helper_write_image_file( + unsigned char /* in */ *data, /* source pixel data */ + int /* in */ width, /* source data width */ + int /* in */ height, /* source data height */ + unsigned char /* in */ *palette, /* palette of source data */ + int /* in */ pixelformat, /* source pixelFormat */ + char const /* in */ *filename, /* destination filename */ + int /* in */ imageformat, /* one of enum imageformat */ + sixel_allocator_t /* in */ *allocator); /* allocator object */ + +#ifdef __cplusplus +} +#endif + + +/* easy encoder API */ + +/* handle type of dither context object */ +struct sixel_encoder; +typedef struct sixel_encoder sixel_encoder_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/* create encoder object */ +SIXELAPI SIXELSTATUS +sixel_encoder_new( + sixel_encoder_t /* out */ **ppencoder, /* encoder object to be created */ + sixel_allocator_t /* in */ *allocator); /* allocator, null if you use + default allocator */ + +/* deprecated version of sixel_decoder_new() */ +SIXELAPI __attribute__((deprecated)) sixel_encoder_t * +sixel_encoder_create(void); + +/* increase reference count of encoder object (thread-unsafe) */ +SIXELAPI void +sixel_encoder_ref(sixel_encoder_t /* in */ *encoder); + +/* decrease reference count of encoder object (thread-unsafe) */ +SIXELAPI void +sixel_encoder_unref(sixel_encoder_t /* in */ *encoder); + +/* set cancel state flag to encoder object */ +SIXELAPI SIXELSTATUS +sixel_encoder_set_cancel_flag( + sixel_encoder_t /* in */ *encoder, + int /* in */ *cancel_flag); + +/* set an option flag to encoder object */ +SIXELAPI SIXELSTATUS +sixel_encoder_setopt( + sixel_encoder_t /* in */ *encoder, + int /* in */ arg, + char const /* in */ *optarg); + +/* load source data from specified file and encode it to SIXEL format */ +SIXELAPI SIXELSTATUS +sixel_encoder_encode( + sixel_encoder_t /* in */ *encoder, + char const /* in */ *filename); + +/* encode specified pixel data to SIXEL format + * output to encoder->outfd */ +SIXELAPI SIXELSTATUS +sixel_encoder_encode_bytes( + sixel_encoder_t /* in */ *encoder, + unsigned char /* in */ *bytes, + int /* in */ width, + int /* in */ height, + int /* in */ pixelformat, + unsigned char /* in */ *palette, + int /* in */ ncolors); + +#ifdef __cplusplus +} +#endif + + +/* easy encoder API */ + +/* handle type of dither context object */ +struct sixel_decoder; +typedef struct sixel_decoder sixel_decoder_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/* create decoder object */ +SIXELAPI SIXELSTATUS +sixel_decoder_new( + sixel_decoder_t /* out */ **ppdecoder, /* decoder object to be created */ + sixel_allocator_t /* in */ *allocator); /* allocator, null if you use + default allocator */ + +/* deprecated version of sixel_decoder_new() */ +SIXELAPI __attribute__((deprecated)) sixel_decoder_t * +sixel_decoder_create(void); + +/* increase reference count of decoder object (thread-unsafe) */ +SIXELAPI void +sixel_decoder_ref(sixel_decoder_t *decoder); + +/* decrease reference count of decoder object (thread-unsafe) */ +SIXELAPI void +sixel_decoder_unref(sixel_decoder_t *decoder); + +/* set an option flag to decoder object */ +SIXELAPI SIXELSTATUS +sixel_decoder_setopt( + sixel_decoder_t /* in */ *decoder, /* decoder object */ + int /* in */ arg, /* one of SIXEL_OPTFLAG_*** */ + char const /* in */ *optarg); /* null or an argument of optflag */ + +/* load source data from stdin or the file specified with + SIXEL_OPTFLAG_INPUT flag, and decode it */ +SIXELAPI SIXELSTATUS +sixel_decoder_decode( + sixel_decoder_t /* in */ *decoder); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBSIXEL_SIXEL_H */ + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ diff --git a/src/3rdparty/sixel/status.c b/src/3rdparty/sixel/status.c new file mode 100644 index 000000000..509e627a5 --- /dev/null +++ b/src/3rdparty/sixel/status.c @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2014-2018 Hayaki Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +/* STDC_HEADERS */ +#include +#include + +#if HAVE_MEMORY_H +# include +#endif /* HAVE_MEMORY_H */ +#ifdef HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_ERRNO_H +# include +#endif /* HAVE_ERRNO_H */ +#ifdef HAVE_LIBCURL +# include +#endif /* HAVE_LIBCURL */ + +#include +#include "status.h" + +#define SIXEL_MESSAGE_OK ("succeeded") +#define SIXEL_MESSAGE_FALSE ("unexpected error (SIXEL_FALSE)"); +#define SIXEL_MESSAGE_UNEXPECTED ("unexpected error") +#define SIXEL_MESSAGE_INTERRUPTED ("interrupted by a signal") +#define SIXEL_MESSAGE_BAD_ALLOCATION ("runtime error: bad allocation error") +#define SIXEL_MESSAGE_BAD_ARGUMENT ("runtime error: bad argument detected") +#define SIXEL_MESSAGE_BAD_INPUT ("runtime error: bad input detected") +#define SIXEL_MESSAGE_BAD_INTEGER_OVERFLOW ("runtime error: integer overflow") +#define SIXEL_MESSAGE_RUNTIME_ERROR ("runtime error") +#define SIXEL_MESSAGE_LOGIC_ERROR ("logic error") +#define SIXEL_MESSAGE_NOT_IMPLEMENTED ("feature error: not implemented") +#define SIXEL_MESSAGE_FEATURE_ERROR ("feature error") +#define SIXEL_MESSAGE_STBI_ERROR ("stb_image error") +#define SIXEL_MESSAGE_STBIW_ERROR ("stb_image_write error") +#define SIXEL_MESSAGE_JPEG_ERROR ("libjpeg error") +#define SIXEL_MESSAGE_PNG_ERROR ("libpng error") +#define SIXEL_MESSAGE_GDK_ERROR ("GDK error") +#define SIXEL_MESSAGE_GD_ERROR ("GD error") + + +static char g_buffer[1024] = { 0x0 }; + +/* set detailed error message (thread-unsafe) */ +SIXELAPI void +sixel_helper_set_additional_message( + const char /* in */ *message /* error message */ +) +{ + size_t len; + + if (message == 0) + return; + len = strlen(message); + memcpy(g_buffer, message, len < sizeof(g_buffer) ? len: sizeof(g_buffer) - 1); + g_buffer[sizeof(g_buffer) - 1] = 0; +} + + +/* get detailed error message (thread-unsafe) */ +SIXELAPI char const * +sixel_helper_get_additional_message(void) +{ + return g_buffer; +} + + +/* convert error status code int formatted string */ +SIXELAPI char const * +sixel_helper_format_error( + SIXELSTATUS /* in */ status /* status code */ +) +{ + static char buffer[1024]; + char const *error_string; + char *p; + size_t len; + + switch (status & 0x1000) { + case SIXEL_OK: + switch (status) { + case SIXEL_INTERRUPTED: + error_string = SIXEL_MESSAGE_INTERRUPTED; + break; + case SIXEL_OK: + default: + error_string = SIXEL_MESSAGE_OK; + break; + } + break; + case SIXEL_FALSE: + switch (status & 0x1f00) { + case SIXEL_RUNTIME_ERROR: + switch (status) { + case SIXEL_BAD_ALLOCATION: + error_string = SIXEL_MESSAGE_BAD_ALLOCATION; + break; + case SIXEL_BAD_ARGUMENT: + error_string = SIXEL_MESSAGE_BAD_ARGUMENT; + break; + case SIXEL_BAD_INPUT: + error_string = SIXEL_MESSAGE_BAD_INPUT; + break; + case SIXEL_BAD_INTEGER_OVERFLOW: + error_string = SIXEL_MESSAGE_BAD_INTEGER_OVERFLOW; + break; + default: + error_string = SIXEL_MESSAGE_RUNTIME_ERROR; + break; + } + break; + case SIXEL_LOGIC_ERROR: + error_string = SIXEL_MESSAGE_LOGIC_ERROR; + break; + case SIXEL_FEATURE_ERROR: + switch (status) { + case SIXEL_NOT_IMPLEMENTED: + error_string = SIXEL_MESSAGE_NOT_IMPLEMENTED; + break; + default: + error_string = SIXEL_MESSAGE_FEATURE_ERROR; + break; + } + break; + case SIXEL_LIBC_ERROR: + p = strerror(errno); + len = strlen(p) + 1; + memcpy(buffer, p, len < sizeof(buffer) ? len: sizeof(buffer) - 1); + buffer[sizeof(buffer) - 1] = 0; + error_string = buffer; + break; +#ifdef HAVE_LIBCURL + case SIXEL_CURL_ERROR: + error_string = curl_easy_strerror(status & 0xff); + break; +#endif +#ifdef HAVE_JPEG + case SIXEL_JPEG_ERROR: + error_string = SIXEL_MESSAGE_JPEG_ERROR; + break; +#endif +#ifdef HAVE_LIBPNG + case SIXEL_PNG_ERROR: + error_string = SIXEL_MESSAGE_PNG_ERROR; + break; +#endif +#ifdef HAVE_GDK_PIXBUF2 + case SIXEL_GDK_ERROR: + error_string = SIXEL_MESSAGE_GDK_ERROR; + break; +#endif +#ifdef HAVE_GD + case SIXEL_GD_ERROR: + error_string = SIXEL_MESSAGE_GD_ERROR; + break; +#endif + case SIXEL_STBI_ERROR: + error_string = SIXEL_MESSAGE_STBI_ERROR; + break; + case SIXEL_STBIW_ERROR: + error_string = SIXEL_MESSAGE_STBIW_ERROR; + break; + case SIXEL_FALSE: + error_string = SIXEL_MESSAGE_FALSE; + break; + default: + error_string = SIXEL_MESSAGE_UNEXPECTED; + break; + } + break; + default: + error_string = SIXEL_MESSAGE_UNEXPECTED; + break; + } + return error_string; +} + + +#if HAVE_TESTS +static int +test1(void) +{ + int nret = EXIT_FAILURE; + char const *message; + + message = sixel_helper_format_error(SIXEL_OK); + if (strcmp(message, SIXEL_MESSAGE_OK) != 0) { + goto error; + } + + message = sixel_helper_format_error(SIXEL_INTERRUPTED); + if (strcmp(message, SIXEL_MESSAGE_INTERRUPTED) != 0) { + goto error; + } + return EXIT_SUCCESS; +error: + perror("test1"); + return nret; +} + + +static int +test2(void) +{ + int nret = EXIT_FAILURE; + char const *message; + + message = sixel_helper_format_error(SIXEL_BAD_ALLOCATION); + if (strcmp(message, SIXEL_MESSAGE_BAD_ALLOCATION) != 0) { + goto error; + } + + message = sixel_helper_format_error(SIXEL_BAD_ARGUMENT); + if (strcmp(message, SIXEL_MESSAGE_BAD_ARGUMENT) != 0) { + goto error; + } + + message = sixel_helper_format_error(SIXEL_BAD_INPUT); + if (strcmp(message, SIXEL_MESSAGE_BAD_INPUT) != 0) { + goto error; + } + + message = sixel_helper_format_error(SIXEL_RUNTIME_ERROR); + if (strcmp(message, SIXEL_MESSAGE_RUNTIME_ERROR) != 0) { + goto error; + } + + message = sixel_helper_format_error(SIXEL_LOGIC_ERROR); + if (strcmp(message, SIXEL_MESSAGE_LOGIC_ERROR) != 0) { + goto error; + } + + message = sixel_helper_format_error(SIXEL_NOT_IMPLEMENTED); + if (strcmp(message, SIXEL_MESSAGE_NOT_IMPLEMENTED) != 0) { + goto error; + } + + message = sixel_helper_format_error(SIXEL_FEATURE_ERROR); + if (strcmp(message, SIXEL_MESSAGE_FEATURE_ERROR) != 0) { + goto error; + } + + message = sixel_helper_format_error(SIXEL_LIBC_ERROR); + if (strcmp(message, SIXEL_MESSAGE_UNEXPECTED) == 0) { + goto error; + } + +#ifdef HAVE_LIBCURL + message = sixel_helper_format_error(SIXEL_CURL_ERROR); + if (strcmp(message, SIXEL_MESSAGE_UNEXPECTED) == 0) { + goto error; + } +#endif + +#if HAVE_JPEG + message = sixel_helper_format_error(SIXEL_JPEG_ERROR); + if (strcmp(message, SIXEL_MESSAGE_JPEG_ERROR) != 0) { + goto error; + } +#endif + +#if HAVE_LIBPNG + message = sixel_helper_format_error(SIXEL_PNG_ERROR); + if (strcmp(message, SIXEL_MESSAGE_PNG_ERROR) != 0) { + goto error; + } +#endif + +#if HAVE_GD + message = sixel_helper_format_error(SIXEL_GD_ERROR); + if (strcmp(message, SIXEL_MESSAGE_GD_ERROR) != 0) { + goto error; + } +#endif + +#if HAVE_GDK_PIXBUF2 + message = sixel_helper_format_error(SIXEL_GDK_ERROR); + if (strcmp(message, SIXEL_MESSAGE_GDK_ERROR) != 0) { + goto error; + } +#endif + + message = sixel_helper_format_error(SIXEL_STBI_ERROR); + if (strcmp(message, SIXEL_MESSAGE_STBI_ERROR) != 0) { + goto error; + } + + message = sixel_helper_format_error(SIXEL_STBIW_ERROR); + if (strcmp(message, SIXEL_MESSAGE_STBIW_ERROR) != 0) { + goto error; + } + + return EXIT_SUCCESS; +error: + perror("test2"); + return nret; +} + + +SIXELAPI int +sixel_status_tests_main(void) +{ + int nret = EXIT_FAILURE; + size_t i; + typedef int (* testcase)(void); + + static testcase const testcases[] = { + test1, + test2, + }; + + for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) { + nret = testcases[i](); + if (nret != EXIT_SUCCESS) { + goto error; + } + } + + nret = EXIT_SUCCESS; + +error: + return nret; +} +#endif /* HAVE_TESTS */ + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ diff --git a/src/3rdparty/sixel/status.h b/src/3rdparty/sixel/status.h new file mode 100644 index 000000000..ea2358403 --- /dev/null +++ b/src/3rdparty/sixel/status.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014-2016 Hayaki Saito + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef LIBSIXEL_STATUS_H +#define LIBSIXEL_STATUS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if HAVE_TESTS +int +sixel_status_tests_main(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LIBSIXEL_STATUS_H */ + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ diff --git a/src/3rdparty/sixel/tosixel.c b/src/3rdparty/sixel/tosixel.c new file mode 100644 index 000000000..f3a23731a --- /dev/null +++ b/src/3rdparty/sixel/tosixel.c @@ -0,0 +1,1627 @@ +/* + * this file is derived from "sixel" original version (2014-3-2) + * http://nanno.dip.jp/softlib/man/rlogin/sixel.tar.gz + * + * Initial developer of this file is kmiya@culti. + * + * He distributes it under very permissive license which permits + * useing, copying, modification, redistribution, and all other + * public activities without any restrictions. + * + * He declares this is compatible with MIT/BSD/GPL. + * + * Hayaki Saito (saitoha@me.com) modified this and re-licensed + * it under the MIT license. + * + * Araki Ken added high-color encoding mode(sixel_encode_highcolor) + * extension. + * + */ +#include "config.h" + +/* STDC_HEADERS */ +#include +#include + +#if HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#if HAVE_LIMITS_H +# include +#endif /* HAVE_LIMITS_H */ +#if HAVE_INTTYPES_H +# include +#endif /* HAVE_INTTYPES_H */ + +#include +#include "output.h" +#include "dither.h" + +#define DCS_START_7BIT "\033P" +#define DCS_START_7BIT_SIZE (sizeof(DCS_START_7BIT) - 1) +#define DCS_START_8BIT "\220" +#define DCS_START_8BIT_SIZE (sizeof(DCS_START_8BIT) - 1) +#define DCS_END_7BIT "\033\\" +#define DCS_END_7BIT_SIZE (sizeof(DCS_END_7BIT) - 1) +#define DCS_END_8BIT "\234" +#define DCS_END_8BIT_SIZE (sizeof(DCS_END_8BIT) - 1) +#define DCS_7BIT(x) DCS_START_7BIT x DCS_END_7BIT +#define DCS_8BIT(x) DCS_START_8BIT x DCS_END_8BIT +#define SCREEN_PACKET_SIZE 256 + +enum { + PALETTE_HIT = 1, + PALETTE_CHANGE = 2 +}; + +/* implementation */ + +/* GNU Screen penetration */ +static void +sixel_penetrate( + sixel_output_t /* in */ *output, /* output context */ + int /* in */ nwrite, /* output size */ + char const /* in */ *dcs_start, /* DCS introducer */ + char const /* in */ *dcs_end, /* DCS terminator */ + int const /* in */ dcs_start_size, /* size of DCS introducer */ + int const /* in */ dcs_end_size) /* size of DCS terminator */ +{ + int pos; + int const splitsize = SCREEN_PACKET_SIZE + - dcs_start_size - dcs_end_size; + + for (pos = 0; pos < nwrite; pos += splitsize) { + output->fn_write((char *)dcs_start, dcs_start_size, output->priv); + output->fn_write(((char *)output->buffer) + pos, + nwrite - pos < splitsize ? nwrite - pos: splitsize, + output->priv); + output->fn_write((char *)dcs_end, dcs_end_size, output->priv); + } +} + + +static void +sixel_advance(sixel_output_t *output, int nwrite) +{ + if ((output->pos += nwrite) >= SIXEL_OUTPUT_PACKET_SIZE) { + if (output->penetrate_multiplexer) { + sixel_penetrate(output, + SIXEL_OUTPUT_PACKET_SIZE, + DCS_START_7BIT, + DCS_END_7BIT, + DCS_START_7BIT_SIZE, + DCS_END_7BIT_SIZE); + } else { + output->fn_write((char *)output->buffer, + SIXEL_OUTPUT_PACKET_SIZE, output->priv); + } + memcpy(output->buffer, + output->buffer + SIXEL_OUTPUT_PACKET_SIZE, + (size_t)(output->pos -= SIXEL_OUTPUT_PACKET_SIZE)); + } +} + + +static void +sixel_putc(unsigned char *buffer, unsigned char value) +{ + *buffer = value; +} + + +static void +sixel_puts(unsigned char *buffer, char const *value, int size) +{ + memcpy(buffer, (void *)value, (size_t)size); +} + + +#if HAVE_LDIV +static int +sixel_putnum_impl(char *buffer, long value, int pos) +{ + ldiv_t r; + + r = ldiv(value, 10); + if (r.quot > 0) { + pos = sixel_putnum_impl(buffer, r.quot, pos); + } + *(buffer + pos) = '0' + r.rem; + return pos + 1; +} +#endif /* HAVE_LDIV */ + + +static int +sixel_putnum(char *buffer, int value) +{ + int pos; + +#if HAVE_LDIV + pos = sixel_putnum_impl(buffer, value, 0); +#else + pos = sprintf(buffer, "%d", value); +#endif /* HAVE_LDIV */ + + return pos; +} + + +static SIXELSTATUS +sixel_put_flash(sixel_output_t *const output) +{ + int n; + int nwrite; + + if (output->has_gri_arg_limit) { /* VT240 Max 255 ? */ + while (output->save_count > 255) { + /* argument of DECGRI('!') is limitted to 255 in real VT */ + sixel_puts(output->buffer + output->pos, "!255", 4); + sixel_advance(output, 4); + sixel_putc(output->buffer + output->pos, output->save_pixel); + sixel_advance(output, 1); + output->save_count -= 255; + } + } + + if (output->save_count > 3) { + /* DECGRI Graphics Repeat Introducer ! Pn Ch */ + sixel_putc(output->buffer + output->pos, '!'); + sixel_advance(output, 1); + nwrite = sixel_putnum((char *)output->buffer + output->pos, output->save_count); + sixel_advance(output, nwrite); + sixel_putc(output->buffer + output->pos, output->save_pixel); + sixel_advance(output, 1); + } else { + for (n = 0; n < output->save_count; n++) { + output->buffer[output->pos] = output->save_pixel; + sixel_advance(output, 1); + } + } + + output->save_pixel = 0; + output->save_count = 0; + + return 0; +} + + +static SIXELSTATUS +sixel_put_pixel(sixel_output_t *const output, int pix) +{ + SIXELSTATUS status = SIXEL_FALSE; + + if (pix < 0 || pix > '?') { + pix = 0; + } + + pix += '?'; + + if (pix == output->save_pixel) { + output->save_count++; + } else { + status = sixel_put_flash(output); + if (SIXEL_FAILED(status)) { + goto end; + } + output->save_pixel = pix; + output->save_count = 1; + } + + status = SIXEL_OK; + +end: + return status; +} + +static SIXELSTATUS +sixel_node_new(sixel_node_t **np, sixel_allocator_t *allocator) +{ + SIXELSTATUS status = SIXEL_FALSE; + + *np = (sixel_node_t *)sixel_allocator_malloc(allocator, + sizeof(sixel_node_t)); + if (np == NULL) { + sixel_helper_set_additional_message( + "sixel_node_new: sixel_allocator_malloc() failed."); + status = SIXEL_BAD_ALLOCATION; + goto end; + } + + status = SIXEL_OK; + +end: + return status; +} + +static void +sixel_node_del(sixel_output_t *output, sixel_node_t *np) +{ + sixel_node_t *tp; + + if ((tp = output->node_top) == np) { + output->node_top = np->next; + } else { + while (tp->next != NULL) { + if (tp->next == np) { + tp->next = np->next; + break; + } + tp = tp->next; + } + } + + np->next = output->node_free; + output->node_free = np; +} + + +static SIXELSTATUS +sixel_put_node( + sixel_output_t /* in */ *output, /* output context */ + int /* in/out */ *x, /* header position */ + sixel_node_t /* in */ *np, /* node object */ + int /* in */ ncolors, /* number of palette colors */ + int /* in */ keycolor) /* transparent color number */ +{ + SIXELSTATUS status = SIXEL_FALSE; + int nwrite; + + if (ncolors != 2 || keycolor == (-1)) { + /* designate palette index */ + if (output->active_palette != np->pal) { + sixel_putc(output->buffer + output->pos, '#'); + sixel_advance(output, 1); + nwrite = sixel_putnum((char *)output->buffer + output->pos, np->pal); + sixel_advance(output, nwrite); + output->active_palette = np->pal; + } + } + + for (; *x < np->sx; ++*x) { + status = sixel_put_pixel(output, 0); + if (SIXEL_FAILED(status)) { + goto end; + } + } + + for (; *x < np->mx; ++*x) { + status = sixel_put_pixel(output, np->map[*x]); + if (SIXEL_FAILED(status)) { + goto end; + } + } + + status = sixel_put_flash(output); + if (SIXEL_FAILED(status)) { + goto end; + } + +end: + return status; +} + + +static SIXELSTATUS +sixel_encode_header(int width, int height, sixel_output_t *output) +{ + SIXELSTATUS status = SIXEL_FALSE; + int nwrite; + int p[3] = {0, 0, 0}; + int pcount = 3; + int use_raster_attributes = 1; + + output->pos = 0; + + if (!output->skip_dcs_envelope) { + if (output->has_8bit_control) { + sixel_puts(output->buffer + output->pos, + DCS_START_8BIT, + DCS_START_8BIT_SIZE); + sixel_advance(output, DCS_START_8BIT_SIZE); + } else { + sixel_puts(output->buffer + output->pos, + DCS_START_7BIT, + DCS_START_7BIT_SIZE); + sixel_advance(output, DCS_START_7BIT_SIZE); + } + } + + if (p[2] == 0) { + pcount--; + if (p[1] == 0) { + pcount--; + if (p[0] == 0) { + pcount--; + } + } + } + + if (pcount > 0) { + nwrite = sixel_putnum((char *)output->buffer + output->pos, p[0]); + sixel_advance(output, nwrite); + if (pcount > 1) { + sixel_putc(output->buffer + output->pos, ';'); + sixel_advance(output, 1); + nwrite = sixel_putnum((char *)output->buffer + output->pos, p[1]); + sixel_advance(output, nwrite); + if (pcount > 2) { + sixel_putc(output->buffer + output->pos, ';'); + sixel_advance(output, 1); + nwrite = sixel_putnum((char *)output->buffer + output->pos, p[2]); + sixel_advance(output, nwrite); + } + } + } + + sixel_putc(output->buffer + output->pos, 'q'); + sixel_advance(output, 1); + + if (use_raster_attributes) { + sixel_puts(output->buffer + output->pos, "\"1;1;", 5); + sixel_advance(output, 5); + nwrite = sixel_putnum((char *)output->buffer + output->pos, width); + sixel_advance(output, nwrite); + sixel_putc(output->buffer + output->pos, ';'); + sixel_advance(output, 1); + nwrite = sixel_putnum((char *)output->buffer + output->pos, height); + sixel_advance(output, nwrite); + } + + status = SIXEL_OK; + + return status; +} + + +static SIXELSTATUS +output_rgb_palette_definition( + sixel_output_t /* in */ *output, + unsigned char /* in */ *palette, + int /* in */ n, + int /* in */ keycolor +) +{ + SIXELSTATUS status = SIXEL_FALSE; + int nwrite; + + if (n != keycolor) { + /* DECGCI Graphics Color Introducer # Pc ; Pu; Px; Py; Pz */ + sixel_putc(output->buffer + output->pos, '#'); + sixel_advance(output, 1); + nwrite = sixel_putnum((char *)output->buffer + output->pos, n); + sixel_advance(output, nwrite); + sixel_puts(output->buffer + output->pos, ";2;", 3); + sixel_advance(output, 3); + nwrite = sixel_putnum((char *)output->buffer + output->pos, + (palette[n * 3 + 0] * 100 + 127) / 255); + sixel_advance(output, nwrite); + sixel_putc(output->buffer + output->pos, ';'); + sixel_advance(output, 1); + nwrite = sixel_putnum((char *)output->buffer + output->pos, + (palette[n * 3 + 1] * 100 + 127) / 255); + sixel_advance(output, nwrite); + sixel_putc(output->buffer + output->pos, ';'); + sixel_advance(output, 1); + nwrite = sixel_putnum((char *)output->buffer + output->pos, + (palette[n * 3 + 2] * 100 + 127) / 255); + sixel_advance(output, nwrite); + } + + status = SIXEL_OK; + + return status; +} + + +static SIXELSTATUS +output_hls_palette_definition( + sixel_output_t /* in */ *output, + unsigned char /* in */ *palette, + int /* in */ n, + int /* in */ keycolor +) +{ + SIXELSTATUS status = SIXEL_FALSE; + int h; + int l; + int s; + int r; + int g; + int b; + int max; + int min; + int nwrite; + + if (n != keycolor) { + r = palette[n * 3 + 0]; + g = palette[n * 3 + 1]; + b = palette[n * 3 + 2]; + max = r > g ? (r > b ? r: b): (g > b ? g: b); + min = r < g ? (r < b ? r: b): (g < b ? g: b); + l = ((max + min) * 100 + 255) / 510; + if (max == min) { + h = s = 0; + } else { + if (l < 50) { + s = ((max - min) * 100) / (max + min); + } else { + s = ((max - min) * 100) / ((255 - max) + (255 - min)); + } + if (r == max) { + h = 120 + (g - b) * 60 / (max - min); + } else if (g == max) { + h = 240 + (b - r) * 60 / (max - min); + } else if (r < g) /* if (b == max) */ { + h = 360 + (r - g) * 60 / (max - min); + } else { + h = 0 + (r - g) * 60 / (max - min); + } + } + /* DECGCI Graphics Color Introducer # Pc ; Pu; Px; Py; Pz */ + sixel_putc(output->buffer + output->pos, '#'); + sixel_advance(output, 1); + nwrite = sixel_putnum((char *)output->buffer + output->pos, n); + sixel_advance(output, nwrite); + sixel_puts(output->buffer + output->pos, ";1;", 3); + sixel_advance(output, 3); + nwrite = sixel_putnum((char *)output->buffer + output->pos, h); + sixel_advance(output, nwrite); + sixel_putc(output->buffer + output->pos, ';'); + sixel_advance(output, 1); + nwrite = sixel_putnum((char *)output->buffer + output->pos, l); + sixel_advance(output, nwrite); + sixel_putc(output->buffer + output->pos, ';'); + sixel_advance(output, 1); + nwrite = sixel_putnum((char *)output->buffer + output->pos, s); + sixel_advance(output, nwrite); + } + + status = SIXEL_OK; + return status; +} + + +static SIXELSTATUS +sixel_encode_body( + sixel_index_t /* in */ *pixels, + int /* in */ width, + int /* in */ height, + unsigned char /* in */ *palette, + int /* in */ ncolors, + int /* in */ keycolor, + int /* in */ bodyonly, + sixel_output_t /* in */ *output, + unsigned char /* in */ *palstate, + sixel_allocator_t /* in */ *allocator) +{ + SIXELSTATUS status = SIXEL_FALSE; + int x; + int y; + int i; + int n; + int c; + int sx; + int mx; + int len; + int pix; + char *map = NULL; + int check_integer_overflow; + sixel_node_t *np, *tp, top; + int fillable; + + if (ncolors < 1) { + status = SIXEL_BAD_ARGUMENT; + goto end; + } + len = ncolors * width; + output->active_palette = (-1); + + map = (char *)sixel_allocator_calloc(allocator, + (size_t)len, + sizeof(char)); + if (map == NULL) { + sixel_helper_set_additional_message( + "sixel_encode_body: sixel_allocator_calloc() failed."); + status = SIXEL_BAD_ALLOCATION; + goto end; + } + + if (!bodyonly && (ncolors != 2 || keycolor == (-1))) { + if (output->palette_type == SIXEL_PALETTETYPE_HLS) { + for (n = 0; n < ncolors; n++) { + status = output_hls_palette_definition(output, palette, n, keycolor); + if (SIXEL_FAILED(status)) { + goto end; + } + } + } else { + for (n = 0; n < ncolors; n++) { + status = output_rgb_palette_definition(output, palette, n, keycolor); + if (SIXEL_FAILED(status)) { + goto end; + } + } + } + } + + for (y = i = 0; y < height; y++) { + if (output->encode_policy != SIXEL_ENCODEPOLICY_SIZE) { + fillable = 0; + } else if (palstate) { + /* high color sixel */ + pix = pixels[(y - i) * width]; + if (pix >= ncolors) { + fillable = 0; + } else { + fillable = 1; + } + } else { + /* normal sixel */ + fillable = 1; + } + for (x = 0; x < width; x++) { + if (y > INT_MAX / width) { + /* integer overflow */ + sixel_helper_set_additional_message( + "sixel_encode_body: integer overflow detected." + " (y > INT_MAX)"); + status = SIXEL_BAD_INTEGER_OVERFLOW; + goto end; + } + check_integer_overflow = y * width; + if (check_integer_overflow > INT_MAX - x) { + /* integer overflow */ + sixel_helper_set_additional_message( + "sixel_encode_body: integer overflow detected." + " (y * width > INT_MAX - x)"); + status = SIXEL_BAD_INTEGER_OVERFLOW; + goto end; + } + pix = pixels[check_integer_overflow + x]; /* color index */ + if (pix >= 0 && pix < ncolors && pix != keycolor) { + if (pix > INT_MAX / width) { + /* integer overflow */ + sixel_helper_set_additional_message( + "sixel_encode_body: integer overflow detected." + " (pix > INT_MAX / width)"); + status = SIXEL_BAD_INTEGER_OVERFLOW; + goto end; + } + check_integer_overflow = pix * width; + if (check_integer_overflow > INT_MAX - x) { + /* integer overflow */ + sixel_helper_set_additional_message( + "sixel_encode_body: integer overflow detected." + " (pix * width > INT_MAX - x)"); + status = SIXEL_BAD_INTEGER_OVERFLOW; + goto end; + } + map[pix * width + x] |= (1 << i); + } + else if (!palstate) { + fillable = 0; + } + } + + if (++i < 6 && (y + 1) < height) { + continue; + } + + for (c = 0; c < ncolors; c++) { + for (sx = 0; sx < width; sx++) { + if (*(map + c * width + sx) == 0) { + continue; + } + + for (mx = sx + 1; mx < width; mx++) { + if (*(map + c * width + mx) != 0) { + continue; + } + + for (n = 1; (mx + n) < width; n++) { + if (*(map + c * width + mx + n) != 0) { + break; + } + } + + if (n >= 10 || (mx + n) >= width) { + break; + } + mx = mx + n - 1; + } + + if ((np = output->node_free) != NULL) { + output->node_free = np->next; + } else { + status = sixel_node_new(&np, allocator); + if (SIXEL_FAILED(status)) { + goto end; + } + } + + np->pal = c; + np->sx = sx; + np->mx = mx; + np->map = map + c * width; + + top.next = output->node_top; + tp = ⊤ + + while (tp->next != NULL) { + if (np->sx < tp->next->sx) { + break; + } else if (np->sx == tp->next->sx && np->mx > tp->next->mx) { + break; + } + tp = tp->next; + } + + np->next = tp->next; + tp->next = np; + output->node_top = top.next; + + sx = mx - 1; + } + + } + + if (y != 5) { + /* DECGNL Graphics Next Line */ + output->buffer[output->pos] = '-'; + sixel_advance(output, 1); + } + + for (x = 0; (np = output->node_top) != NULL;) { + sixel_node_t *next; + if (x > np->sx) { + /* DECGCR Graphics Carriage Return */ + output->buffer[output->pos] = '$'; + sixel_advance(output, 1); + x = 0; + } + + if (fillable) { + memset(np->map + np->sx, (1 << i) - 1, (size_t)(np->mx - np->sx)); + } + status = sixel_put_node(output, &x, np, ncolors, keycolor); + if (SIXEL_FAILED(status)) { + goto end; + } + next = np->next; + sixel_node_del(output, np); + np = next; + + while (np != NULL) { + if (np->sx < x) { + np = np->next; + continue; + } + + if (fillable) { + memset(np->map + np->sx, (1 << i) - 1, (size_t)(np->mx - np->sx)); + } + status = sixel_put_node(output, &x, np, ncolors, keycolor); + if (SIXEL_FAILED(status)) { + goto end; + } + next = np->next; + sixel_node_del(output, np); + np = next; + } + + fillable = 0; + } + + i = 0; + memset(map, 0, (size_t)len); + } + + if (palstate) { + output->buffer[output->pos] = '$'; + sixel_advance(output, 1); + } + + status = SIXEL_OK; + +end: + /* free nodes */ + while ((np = output->node_free) != NULL) { + output->node_free = np->next; + sixel_allocator_free(allocator, np); + } + output->node_top = NULL; + + sixel_allocator_free(allocator, map); + + return status; +} + + +static SIXELSTATUS +sixel_encode_footer(sixel_output_t *output) +{ + SIXELSTATUS status = SIXEL_FALSE; + + if (!output->skip_dcs_envelope && !output->penetrate_multiplexer) { + if (output->has_8bit_control) { + sixel_puts(output->buffer + output->pos, + DCS_END_8BIT, DCS_END_8BIT_SIZE); + sixel_advance(output, DCS_END_8BIT_SIZE); + } else { + sixel_puts(output->buffer + output->pos, + DCS_END_7BIT, DCS_END_7BIT_SIZE); + sixel_advance(output, DCS_END_7BIT_SIZE); + } + } + + /* flush buffer */ + if (output->pos > 0) { + if (output->penetrate_multiplexer) { + sixel_penetrate(output, output->pos, + DCS_START_7BIT, + DCS_END_7BIT, + DCS_START_7BIT_SIZE, + DCS_END_7BIT_SIZE); + output->fn_write((char *)DCS_7BIT("\033") DCS_7BIT("\\"), + (DCS_START_7BIT_SIZE + 1 + DCS_END_7BIT_SIZE) * 2, + output->priv); + } else { + output->fn_write((char *)output->buffer, output->pos, output->priv); + } + } + + status = SIXEL_OK; + + return status; +} + + +static SIXELSTATUS +sixel_encode_dither( + unsigned char /* in */ *pixels, /* pixel bytes to be encoded */ + int /* in */ width, /* width of source image */ + int /* in */ height, /* height of source image */ + sixel_dither_t /* in */ *dither, /* dither context */ + sixel_output_t /* in */ *output) /* output context */ +{ + SIXELSTATUS status = SIXEL_FALSE; + sixel_index_t *paletted_pixels = NULL; + sixel_index_t *input_pixels; + size_t bufsize; + + switch (dither->pixelformat) { + case SIXEL_PIXELFORMAT_PAL1: + case SIXEL_PIXELFORMAT_PAL2: + case SIXEL_PIXELFORMAT_PAL4: + case SIXEL_PIXELFORMAT_G1: + case SIXEL_PIXELFORMAT_G2: + case SIXEL_PIXELFORMAT_G4: + bufsize = (sizeof(sixel_index_t) * (size_t)width * (size_t)height * 3UL); + paletted_pixels = (sixel_index_t *)sixel_allocator_malloc(dither->allocator, bufsize); + if (paletted_pixels == NULL) { + sixel_helper_set_additional_message( + "sixel_encode_dither: sixel_allocator_malloc() failed."); + status = SIXEL_BAD_ALLOCATION; + goto end; + } + status = sixel_helper_normalize_pixelformat(paletted_pixels, + &dither->pixelformat, + pixels, + dither->pixelformat, + width, height); + if (SIXEL_FAILED(status)) { + goto end; + } + input_pixels = paletted_pixels; + break; + case SIXEL_PIXELFORMAT_PAL8: + case SIXEL_PIXELFORMAT_G8: + case SIXEL_PIXELFORMAT_GA88: + case SIXEL_PIXELFORMAT_AG88: + input_pixels = pixels; + break; + default: + /* apply palette */ + paletted_pixels = sixel_dither_apply_palette(dither, pixels, + width, height); + if (paletted_pixels == NULL) { + status = SIXEL_RUNTIME_ERROR; + goto end; + } + input_pixels = paletted_pixels; + break; + } + + status = sixel_encode_header(width, height, output); + if (SIXEL_FAILED(status)) { + goto end; + } + + status = sixel_encode_body(input_pixels, + width, + height, + dither->palette, + dither->ncolors, + dither->keycolor, + dither->bodyonly, + output, + NULL, + dither->allocator); + if (SIXEL_FAILED(status)) { + goto end; + } + + status = sixel_encode_footer(output); + if (SIXEL_FAILED(status)) { + goto end; + } + +end: + sixel_allocator_free(dither->allocator, paletted_pixels); + + return status; +} + +static void +dither_func_none(unsigned char *data, int width) +{ + (void) data; /* unused */ + (void) width; /* unused */ +} + + +static void +dither_func_fs(unsigned char *data, int width) +{ + int r, g, b; + int error_r = data[0] & 0x7; + int error_g = data[1] & 0x7; + int error_b = data[2] & 0x7; + + /* Floyd Steinberg Method + * curr 7/16 + * 3/16 5/48 1/16 + */ + r = (data[3 + 0] + (error_r * 5 >> 4)); + g = (data[3 + 1] + (error_g * 5 >> 4)); + b = (data[3 + 2] + (error_b * 5 >> 4)); + data[3 + 0] = r > 0xff ? 0xff: r; + data[3 + 1] = g > 0xff ? 0xff: g; + data[3 + 2] = b > 0xff ? 0xff: b; + r = data[width * 3 - 3 + 0] + (error_r * 3 >> 4); + g = data[width * 3 - 3 + 1] + (error_g * 3 >> 4); + b = data[width * 3 - 3 + 2] + (error_b * 3 >> 4); + data[width * 3 - 3 + 0] = r > 0xff ? 0xff: r; + data[width * 3 - 3 + 1] = g > 0xff ? 0xff: g; + data[width * 3 - 3 + 2] = b > 0xff ? 0xff: b; + r = data[width * 3 + 0] + (error_r * 5 >> 4); + g = data[width * 3 + 1] + (error_g * 5 >> 4); + b = data[width * 3 + 2] + (error_b * 5 >> 4); + data[width * 3 + 0] = r > 0xff ? 0xff: r; + data[width * 3 + 1] = g > 0xff ? 0xff: g; + data[width * 3 + 2] = b > 0xff ? 0xff: b; +} + + +static void +dither_func_atkinson(unsigned char *data, int width) +{ + int r, g, b; + int error_r = data[0] & 0x7; + int error_g = data[1] & 0x7; + int error_b = data[2] & 0x7; + + error_r += 4; + error_g += 4; + error_b += 4; + + /* Atkinson's Method + * curr 1/8 1/8 + * 1/8 1/8 1/8 + * 1/8 + */ + r = data[(width * 0 + 1) * 3 + 0] + (error_r >> 3); + g = data[(width * 0 + 1) * 3 + 1] + (error_g >> 3); + b = data[(width * 0 + 1) * 3 + 2] + (error_b >> 3); + data[(width * 0 + 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 0 + 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 0 + 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 0 + 2) * 3 + 0] + (error_r >> 3); + g = data[(width * 0 + 2) * 3 + 1] + (error_g >> 3); + b = data[(width * 0 + 2) * 3 + 2] + (error_b >> 3); + data[(width * 0 + 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 0 + 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 0 + 2) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 - 1) * 3 + 0] + (error_r >> 3); + g = data[(width * 1 - 1) * 3 + 1] + (error_g >> 3); + b = data[(width * 1 - 1) * 3 + 2] + (error_b >> 3); + data[(width * 1 - 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 - 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 - 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 + 0) * 3 + 0] + (error_r >> 3); + g = data[(width * 1 + 0) * 3 + 1] + (error_g >> 3); + b = data[(width * 1 + 0) * 3 + 2] + (error_b >> 3); + data[(width * 1 + 0) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 + 0) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 + 0) * 3 + 2] = b > 0xff ? 0xff: b; + r = (data[(width * 1 + 1) * 3 + 0] + (error_r >> 3)); + g = (data[(width * 1 + 1) * 3 + 1] + (error_g >> 3)); + b = (data[(width * 1 + 1) * 3 + 2] + (error_b >> 3)); + data[(width * 1 + 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 + 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 + 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = (data[(width * 2 + 0) * 3 + 0] + (error_r >> 3)); + g = (data[(width * 2 + 0) * 3 + 1] + (error_g >> 3)); + b = (data[(width * 2 + 0) * 3 + 2] + (error_b >> 3)); + data[(width * 2 + 0) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 2 + 0) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 2 + 0) * 3 + 2] = b > 0xff ? 0xff: b; +} + + +static void +dither_func_jajuni(unsigned char *data, int width) +{ + int r, g, b; + int error_r = data[0] & 0x7; + int error_g = data[1] & 0x7; + int error_b = data[2] & 0x7; + + error_r += 4; + error_g += 4; + error_b += 4; + + /* Jarvis, Judice & Ninke Method + * curr 7/48 5/48 + * 3/48 5/48 7/48 5/48 3/48 + * 1/48 3/48 5/48 3/48 1/48 + */ + r = data[(width * 0 + 1) * 3 + 0] + (error_r * 7 / 48); + g = data[(width * 0 + 1) * 3 + 1] + (error_g * 7 / 48); + b = data[(width * 0 + 1) * 3 + 2] + (error_b * 7 / 48); + data[(width * 0 + 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 0 + 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 0 + 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 0 + 2) * 3 + 0] + (error_r * 5 / 48); + g = data[(width * 0 + 2) * 3 + 1] + (error_g * 5 / 48); + b = data[(width * 0 + 2) * 3 + 2] + (error_b * 5 / 48); + data[(width * 0 + 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 0 + 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 0 + 2) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 - 2) * 3 + 0] + (error_r * 3 / 48); + g = data[(width * 1 - 2) * 3 + 1] + (error_g * 3 / 48); + b = data[(width * 1 - 2) * 3 + 2] + (error_b * 3 / 48); + data[(width * 1 - 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 - 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 - 2) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 - 1) * 3 + 0] + (error_r * 5 / 48); + g = data[(width * 1 - 1) * 3 + 1] + (error_g * 5 / 48); + b = data[(width * 1 - 1) * 3 + 2] + (error_b * 5 / 48); + data[(width * 1 - 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 - 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 - 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 + 0) * 3 + 0] + (error_r * 7 / 48); + g = data[(width * 1 + 0) * 3 + 1] + (error_g * 7 / 48); + b = data[(width * 1 + 0) * 3 + 2] + (error_b * 7 / 48); + data[(width * 1 + 0) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 + 0) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 + 0) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 + 1) * 3 + 0] + (error_r * 5 / 48); + g = data[(width * 1 + 1) * 3 + 1] + (error_g * 5 / 48); + b = data[(width * 1 + 1) * 3 + 2] + (error_b * 5 / 48); + data[(width * 1 + 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 + 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 + 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 + 2) * 3 + 0] + (error_r * 3 / 48); + g = data[(width * 1 + 2) * 3 + 1] + (error_g * 3 / 48); + b = data[(width * 1 + 2) * 3 + 2] + (error_b * 3 / 48); + data[(width * 1 + 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 + 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 + 2) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 2 - 2) * 3 + 0] + (error_r * 1 / 48); + g = data[(width * 2 - 2) * 3 + 1] + (error_g * 1 / 48); + b = data[(width * 2 - 2) * 3 + 2] + (error_b * 1 / 48); + data[(width * 2 - 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 2 - 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 2 - 2) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 2 - 1) * 3 + 0] + (error_r * 3 / 48); + g = data[(width * 2 - 1) * 3 + 1] + (error_g * 3 / 48); + b = data[(width * 2 - 1) * 3 + 2] + (error_b * 3 / 48); + data[(width * 2 - 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 2 - 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 2 - 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 2 + 0) * 3 + 0] + (error_r * 5 / 48); + g = data[(width * 2 + 0) * 3 + 1] + (error_g * 5 / 48); + b = data[(width * 2 + 0) * 3 + 2] + (error_b * 5 / 48); + data[(width * 2 + 0) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 2 + 0) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 2 + 0) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 2 + 1) * 3 + 0] + (error_r * 3 / 48); + g = data[(width * 2 + 1) * 3 + 1] + (error_g * 3 / 48); + b = data[(width * 2 + 1) * 3 + 2] + (error_b * 3 / 48); + data[(width * 2 + 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 2 + 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 2 + 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 2 + 2) * 3 + 0] + (error_r * 1 / 48); + g = data[(width * 2 + 2) * 3 + 1] + (error_g * 1 / 48); + b = data[(width * 2 + 2) * 3 + 2] + (error_b * 1 / 48); + data[(width * 2 + 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 2 + 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 2 + 2) * 3 + 2] = b > 0xff ? 0xff: b; +} + + +static void +dither_func_stucki(unsigned char *data, int width) +{ + int r, g, b; + int error_r = data[0] & 0x7; + int error_g = data[1] & 0x7; + int error_b = data[2] & 0x7; + + error_r += 4; + error_g += 4; + error_b += 4; + + /* Stucki's Method + * curr 8/48 4/48 + * 2/48 4/48 8/48 4/48 2/48 + * 1/48 2/48 4/48 2/48 1/48 + */ + r = data[(width * 0 + 1) * 3 + 0] + (error_r * 8 / 48); + g = data[(width * 0 + 1) * 3 + 1] + (error_g * 8 / 48); + b = data[(width * 0 + 1) * 3 + 2] + (error_b * 8 / 48); + data[(width * 0 + 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 0 + 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 0 + 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 0 + 2) * 3 + 0] + (error_r * 4 / 48); + g = data[(width * 0 + 2) * 3 + 1] + (error_g * 4 / 48); + b = data[(width * 0 + 2) * 3 + 2] + (error_b * 4 / 48); + data[(width * 0 + 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 0 + 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 0 + 2) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 - 2) * 3 + 0] + (error_r * 2 / 48); + g = data[(width * 1 - 2) * 3 + 1] + (error_g * 2 / 48); + b = data[(width * 1 - 2) * 3 + 2] + (error_b * 2 / 48); + data[(width * 1 - 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 - 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 - 2) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 - 1) * 3 + 0] + (error_r * 4 / 48); + g = data[(width * 1 - 1) * 3 + 1] + (error_g * 4 / 48); + b = data[(width * 1 - 1) * 3 + 2] + (error_b * 4 / 48); + data[(width * 1 - 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 - 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 - 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 + 0) * 3 + 0] + (error_r * 8 / 48); + g = data[(width * 1 + 0) * 3 + 1] + (error_g * 8 / 48); + b = data[(width * 1 + 0) * 3 + 2] + (error_b * 8 / 48); + data[(width * 1 + 0) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 + 0) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 + 0) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 + 1) * 3 + 0] + (error_r * 4 / 48); + g = data[(width * 1 + 1) * 3 + 1] + (error_g * 4 / 48); + b = data[(width * 1 + 1) * 3 + 2] + (error_b * 4 / 48); + data[(width * 1 + 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 + 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 + 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 + 2) * 3 + 0] + (error_r * 2 / 48); + g = data[(width * 1 + 2) * 3 + 1] + (error_g * 2 / 48); + b = data[(width * 1 + 2) * 3 + 2] + (error_b * 2 / 48); + data[(width * 1 + 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 + 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 + 2) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 2 - 2) * 3 + 0] + (error_r * 1 / 48); + g = data[(width * 2 - 2) * 3 + 1] + (error_g * 1 / 48); + b = data[(width * 2 - 2) * 3 + 2] + (error_b * 1 / 48); + data[(width * 2 - 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 2 - 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 2 - 2) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 2 - 1) * 3 + 0] + (error_r * 2 / 48); + g = data[(width * 2 - 1) * 3 + 1] + (error_g * 2 / 48); + b = data[(width * 2 - 1) * 3 + 2] + (error_b * 2 / 48); + data[(width * 2 - 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 2 - 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 2 - 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 2 + 0) * 3 + 0] + (error_r * 4 / 48); + g = data[(width * 2 + 0) * 3 + 1] + (error_g * 4 / 48); + b = data[(width * 2 + 0) * 3 + 2] + (error_b * 4 / 48); + data[(width * 2 + 0) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 2 + 0) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 2 + 0) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 2 + 1) * 3 + 0] + (error_r * 2 / 48); + g = data[(width * 2 + 1) * 3 + 1] + (error_g * 2 / 48); + b = data[(width * 2 + 1) * 3 + 2] + (error_b * 2 / 48); + data[(width * 2 + 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 2 + 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 2 + 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 2 + 2) * 3 + 0] + (error_r * 1 / 48); + g = data[(width * 2 + 2) * 3 + 1] + (error_g * 1 / 48); + b = data[(width * 2 + 2) * 3 + 2] + (error_b * 1 / 48); + data[(width * 2 + 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 2 + 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 2 + 2) * 3 + 2] = b > 0xff ? 0xff: b; +} + + +static void +dither_func_burkes(unsigned char *data, int width) +{ + int r, g, b; + int error_r = data[0] & 0x7; + int error_g = data[1] & 0x7; + int error_b = data[2] & 0x7; + + error_r += 2; + error_g += 2; + error_b += 2; + + /* Burkes' Method + * curr 4/16 2/16 + * 1/16 2/16 4/16 2/16 1/16 + */ + r = data[(width * 0 + 1) * 3 + 0] + (error_r * 4 / 16); + g = data[(width * 0 + 1) * 3 + 1] + (error_g * 4 / 16); + b = data[(width * 0 + 1) * 3 + 2] + (error_b * 4 / 16); + data[(width * 0 + 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 0 + 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 0 + 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 0 + 2) * 3 + 0] + (error_r * 2 / 16); + g = data[(width * 0 + 2) * 3 + 1] + (error_g * 2 / 16); + b = data[(width * 0 + 2) * 3 + 2] + (error_b * 2 / 16); + data[(width * 0 + 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 0 + 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 0 + 2) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 - 2) * 3 + 0] + (error_r * 1 / 16); + g = data[(width * 1 - 2) * 3 + 1] + (error_g * 1 / 16); + b = data[(width * 1 - 2) * 3 + 2] + (error_b * 1 / 16); + data[(width * 1 - 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 - 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 - 2) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 - 1) * 3 + 0] + (error_r * 2 / 16); + g = data[(width * 1 - 1) * 3 + 1] + (error_g * 2 / 16); + b = data[(width * 1 - 1) * 3 + 2] + (error_b * 2 / 16); + data[(width * 1 - 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 - 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 - 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 + 0) * 3 + 0] + (error_r * 4 / 16); + g = data[(width * 1 + 0) * 3 + 1] + (error_g * 4 / 16); + b = data[(width * 1 + 0) * 3 + 2] + (error_b * 4 / 16); + data[(width * 1 + 0) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 + 0) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 + 0) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 + 1) * 3 + 0] + (error_r * 2 / 16); + g = data[(width * 1 + 1) * 3 + 1] + (error_g * 2 / 16); + b = data[(width * 1 + 1) * 3 + 2] + (error_b * 2 / 16); + data[(width * 1 + 1) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 + 1) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 + 1) * 3 + 2] = b > 0xff ? 0xff: b; + r = data[(width * 1 + 2) * 3 + 0] + (error_r * 1 / 16); + g = data[(width * 1 + 2) * 3 + 1] + (error_g * 1 / 16); + b = data[(width * 1 + 2) * 3 + 2] + (error_b * 1 / 16); + data[(width * 1 + 2) * 3 + 0] = r > 0xff ? 0xff: r; + data[(width * 1 + 2) * 3 + 1] = g > 0xff ? 0xff: g; + data[(width * 1 + 2) * 3 + 2] = b > 0xff ? 0xff: b; +} + + +static void +dither_func_a_dither(unsigned char *data, int width, int x, int y) +{ + int c; + float value; + float mask; + + (void) width; /* unused */ + + for (c = 0; c < 3; c ++) { + mask = (((x + c * 17) + y * 236) * 119) & 255; + mask = ((mask - 128) / 256.0f) ; + value = data[c] + mask; + if (value < 0) { + value = 0; + } + value = value > 255 ? 255 : value; + data[c] = value; + } +} + + +static void +dither_func_x_dither(unsigned char *data, int width, int x, int y) +{ + int c; + float value; + float mask; + + (void) width; /* unused */ + + for (c = 0; c < 3; c ++) { + mask = (((x + c * 17) ^ y * 236) * 1234) & 511; + mask = ((mask - 128) / 512.0f) ; + value = data[c] + mask; + if (value < 0) { + value = 0; + } + value = value > 255 ? 255 : value; + data[c] = value; + } +} + + +static void +sixel_apply_15bpp_dither( + unsigned char *pixels, + int x, int y, int width, int height, + int method_for_diffuse) +{ + /* apply floyd steinberg dithering */ + switch (method_for_diffuse) { + case SIXEL_DIFFUSE_FS: + if (x < width - 1 && y < height - 1) { + dither_func_fs(pixels, width); + } + break; + case SIXEL_DIFFUSE_ATKINSON: + if (x < width - 2 && y < height - 2) { + dither_func_atkinson(pixels, width); + } + break; + case SIXEL_DIFFUSE_JAJUNI: + if (x < width - 2 && y < height - 2) { + dither_func_jajuni(pixels, width); + } + break; + case SIXEL_DIFFUSE_STUCKI: + if (x < width - 2 && y < height - 2) { + dither_func_stucki(pixels, width); + } + break; + case SIXEL_DIFFUSE_BURKES: + if (x < width - 2 && y < height - 1) { + dither_func_burkes(pixels, width); + } + break; + case SIXEL_DIFFUSE_A_DITHER: + dither_func_a_dither(pixels, width, x, y); + break; + case SIXEL_DIFFUSE_X_DITHER: + dither_func_x_dither(pixels, width, x, y); + break; + case SIXEL_DIFFUSE_NONE: + default: + dither_func_none(pixels, width); + break; + } +} + + +static SIXELSTATUS +sixel_encode_highcolor( + unsigned char *pixels, int width, int height, + sixel_dither_t *dither, sixel_output_t *output + ) +{ + SIXELSTATUS status = SIXEL_FALSE; + sixel_index_t *paletted_pixels = NULL; + unsigned char *normalized_pixels = NULL; + /* Mark sixel line pixels which have been already drawn. */ + unsigned char *marks; + unsigned char *rgbhit; + unsigned char *rgb2pal; + unsigned char palhitcount[SIXEL_PALETTE_MAX]; + unsigned char palstate[SIXEL_PALETTE_MAX]; + int output_count; + int const maxcolors = 1 << 15; + size_t image_size; + size_t marks_size; + size_t normalized_size; + size_t whole_size; + size_t maxcolors_size; + int x, y; + unsigned char *dst; + unsigned char *mptr; + int dirty; + int mod_y; + int nextpal; + int threshold; + int pix; + int orig_height; + unsigned char *pal; + + /* + * The high-color encoder keeps one palette index per input pixel + * followed by color lookup tables and six scanlines of mark bytes. + * Calculate all derived sizes in size_t and reject arithmetic + * overflow before a wrapped value can reach the allocator. + */ + maxcolors_size = (size_t)maxcolors; + if ((size_t)height > ((size_t)-1) / (size_t)width) { + sixel_helper_set_additional_message( + "sixel_encode_highcolor: image size overflow."); + status = SIXEL_BAD_INPUT; + goto error; + } + image_size = (size_t)width * (size_t)height; + + if (image_size > ((size_t)-1) / 3UL) { + sixel_helper_set_additional_message( + "sixel_encode_highcolor: normalized size overflow."); + status = SIXEL_BAD_INPUT; + goto error; + } + normalized_size = image_size * 3UL; + + if ((size_t)width > ((size_t)-1) / 6UL) { + sixel_helper_set_additional_message( + "sixel_encode_highcolor: marks size overflow."); + status = SIXEL_BAD_INPUT; + goto error; + } + marks_size = (size_t)width * 6UL; + + if (image_size > (size_t)-1 - maxcolors_size || + image_size + maxcolors_size > (size_t)-1 - maxcolors_size || + image_size + maxcolors_size + maxcolors_size + > (size_t)-1 - marks_size) { + sixel_helper_set_additional_message( + "sixel_encode_highcolor: whole size overflow."); + status = SIXEL_BAD_INPUT; + goto error; + } + whole_size = image_size /* for paletted_pixels */ + + maxcolors_size /* for rgbhit */ + + maxcolors_size /* for rgb2pal */ + + marks_size; /* for marks */ + + if (dither->pixelformat != SIXEL_PIXELFORMAT_RGB888) { + /* normalize pixelfromat */ + normalized_pixels = (unsigned char *)sixel_allocator_malloc( + dither->allocator, normalized_size); + if (normalized_pixels == NULL) { + goto error; + } + status = sixel_helper_normalize_pixelformat(normalized_pixels, + &dither->pixelformat, + pixels, + dither->pixelformat, + width, height); + if (SIXEL_FAILED(status)) { + goto error; + } + pixels = normalized_pixels; + } + paletted_pixels = (sixel_index_t *)sixel_allocator_malloc(dither->allocator, + whole_size); + if (paletted_pixels == NULL) { + goto error; + } + rgbhit = paletted_pixels + image_size; + memset(rgbhit, 0, maxcolors_size * 2UL + marks_size); + rgb2pal = rgbhit + maxcolors; + marks = rgb2pal + maxcolors; + output_count = 0; + +next: + dst = paletted_pixels; + nextpal = 0; + threshold = 1; + dirty = 0; + mptr = marks; + memset(palstate, 0, sizeof(palstate)); + y = mod_y = 0; + + while (1) { + for (x = 0; x < width; x++, mptr++, dst++, pixels += 3) { + if (*mptr) { + *dst = 255; + } else { + sixel_apply_15bpp_dither(pixels, + x, y, width, height, + dither->method_for_diffuse); + pix = ((pixels[0] & 0xf8) << 7) | + ((pixels[1] & 0xf8) << 2) | + ((pixels[2] >> 3) & 0x1f); + + if (!rgbhit[pix]) { + while (1) { + if (nextpal >= 255) { + if (threshold >= 255) { + break; + } else { + threshold = (threshold == 1) ? 9: 255; + nextpal = 0; + } + } else if (palstate[nextpal] || + palhitcount[nextpal] > threshold) { + nextpal++; + } else { + break; + } + } + + if (nextpal >= 255) { + dirty = 1; + *dst = 255; + } else { + pal = dither->palette + (nextpal * 3); + + rgbhit[pix] = 1; + if (output_count > 0) { + rgbhit[((pal[0] & 0xf8) << 7) | + ((pal[1] & 0xf8) << 2) | + ((pal[2] >> 3) & 0x1f)] = 0; + } + *dst = rgb2pal[pix] = nextpal++; + *mptr = 1; + palstate[*dst] = PALETTE_CHANGE; + palhitcount[*dst] = 1; + *(pal++) = pixels[0]; + *(pal++) = pixels[1]; + *(pal++) = pixels[2]; + } + } else { + *dst = rgb2pal[pix]; + *mptr = 1; + if (!palstate[*dst]) { + palstate[*dst] = PALETTE_HIT; + } + if (palhitcount[*dst] < 255) { + palhitcount[*dst]++; + } + } + } + } + + if (++y >= height) { + if (dirty) { + mod_y = 5; + } else { + goto end; + } + } + if (dirty && (mod_y == 5 || y >= height)) { + orig_height = height; + + if (output_count++ == 0) { + status = sixel_encode_header(width, height, output); + if (SIXEL_FAILED(status)) { + goto error; + } + } + height = y; + status = sixel_encode_body(paletted_pixels, + width, + height, + dither->palette, + 255, + 255, + dither->bodyonly, + output, + palstate, + dither->allocator); + if (SIXEL_FAILED(status)) { + goto error; + } + if (y >= orig_height) { + goto end; + } + pixels -= (6 * width * 3); + height = orig_height - height + 6; + goto next; + } + + if (++mod_y == 6) { + mptr = (unsigned char *)memset(marks, 0, marks_size); + mod_y = 0; + } + } + + goto next; + +end: + if (output_count == 0) { + status = sixel_encode_header(width, height, output); + if (SIXEL_FAILED(status)) { + goto error; + } + } + status = sixel_encode_body(paletted_pixels, + width, + height, + dither->palette, + 255, + 255, + dither->bodyonly, + output, + palstate, + dither->allocator); + if (SIXEL_FAILED(status)) { + goto error; + } + + status = sixel_encode_footer(output); + if (SIXEL_FAILED(status)) { + goto error; + } + +error: + sixel_allocator_free(dither->allocator, paletted_pixels); + sixel_allocator_free(dither->allocator, normalized_pixels); + + return status; +} + + +SIXELAPI SIXELSTATUS +sixel_encode( + unsigned char /* in */ *pixels, /* pixel bytes */ + int /* in */ width, /* image width */ + int /* in */ height, /* image height */ + int const /* in */ depth, /* color depth */ + sixel_dither_t /* in */ *dither, /* dither context */ + sixel_output_t /* in */ *output) /* output context */ +{ + SIXELSTATUS status = SIXEL_FALSE; + + (void) depth; + + /* TODO: reference counting should be thread-safe */ + sixel_dither_ref(dither); + sixel_output_ref(output); + + if (width < 1) { + sixel_helper_set_additional_message( + "sixel_encode: bad width parameter." + " (width < 1)"); + status = SIXEL_BAD_INPUT; + goto end; + } + + if (height < 1) { + sixel_helper_set_additional_message( + "sixel_encode: bad height parameter." + " (height < 1)"); + status = SIXEL_BAD_INPUT; + goto end; + } + + if (width > SIXEL_WIDTH_LIMIT) { + sixel_helper_set_additional_message( + "sixel_encode: bad width parameter." + " (width > SIXEL_WIDTH_LIMIT)"); + status = SIXEL_BAD_INPUT; + goto end; + } + + if (height > SIXEL_HEIGHT_LIMIT) { + sixel_helper_set_additional_message( + "sixel_encode: bad height parameter." + " (height > SIXEL_HEIGHT_LIMIT)"); + status = SIXEL_BAD_INPUT; + goto end; + } + + if (dither->quality_mode == SIXEL_QUALITY_HIGHCOLOR) { + status = sixel_encode_highcolor(pixels, width, height, + dither, output); + } else { + status = sixel_encode_dither(pixels, width, height, + dither, output); + } + +end: + sixel_output_unref(output); + sixel_dither_unref(dither); + + return status; +} + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */