summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorMr.Smith19742023-09-01 13:38:55 +0300
committerMr.Smith19742023-09-01 13:38:55 +0300
commit39a50ba64f5cc00f6e17222151c91d8d014a558e (patch)
tree7bf696ec561d5ba75987e8a88e7a1c66ec40cda1
parent352067d7722d5b6763349e9d5e6c9a0188d5d3e3 (diff)
downloadaur-betterspades.tar.gz
Updated build
-rw-r--r--.SRCINFO36
-rw-r--r--PKGBUILD56
-rw-r--r--dr_wav.h8800
-rw-r--r--hashtable.c439
-rw-r--r--hashtable.h109
-rw-r--r--http.h719
-rw-r--r--ini.c304
-rw-r--r--ini.h178
-rw-r--r--libdeflate.h411
-rw-r--r--lodepng.cpp6991
-rw-r--r--lodepng.h2089
-rw-r--r--log.c152
-rw-r--r--log.h42
-rw-r--r--microui.c1208
-rw-r--r--microui.h296
-rw-r--r--parson.c2465
-rw-r--r--parson.h274
-rw-r--r--stb_truetype.h5077
18 files changed, 4 insertions, 29642 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 0082ce28f464..54ea704c965c 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -1,7 +1,7 @@
pkgbase = betterspades
pkgdesc = Opensource game client for Ace Of Spades 0.75 protocol
pkgver = 0.1.5
- pkgrel = 2
+ pkgrel = 3
url = https://github.com/xtreme8000/betterspades/
arch = i686
arch = x86_64
@@ -17,44 +17,10 @@ pkgbase = betterspades
depends = enet
depends = glew
source = betterspades-0.1.5.tar.gz::https://github.com/xtreme8000/betterspades/archive/v0.1.5.tar.gz
- source = http://aos.party/bsresources.zip
source = betterspades.desktop
source = betterspades
- source = hashtable.c
- source = hashtable.h
- source = microui.c
- source = microui.h
- source = dr_wav.h
- source = http.h
- source = ini.c
- source = ini.h
- source = libdeflate.h
- source = lodepng.cpp
- source = lodepng.h
- source = log.c
- source = log.h
- source = parson.c
- source = parson.h
- source = stb_truetype.h
sha512sums = 743c66f620be0e15a4b9055ba0e1709fcec6e7152836f4c87ad6545cd2b03fc14691b523b196608c5a62e7774f272227b3b7bc436445e5205549b55cb5284ceb
- sha512sums = 83651b4befe06b534299fac03de032466f20ab8010bcb0828e8f67594865726437b527334fd3f048b5ae1939ad0e4d02bf8fd5cff2e23afba7d6e644dd6f5fa7
sha512sums = 45488d7196410a1bc9461de82ac28ac5335cb99babae5f8a4465539b196a4cbe9361aa1f8d7521289537aae6fdb2a8c5c2588b20f4fd5fe7c4484f9c9c8e285b
sha512sums = 5cac9e619faa2d83a3db76753b9bbea40d3c97de579701357d6d6472feb378bb4dca1ba389b132ae6187b0a15b2bc17d42fba331e0c653682827d09af0e231aa
- sha512sums = 44eb971c90986186b2c532fde9c40ec4f61bc6f1c97b4424cd074c29d7b98fe43cb973dae7b89c9587fcf06c0816b93e9dddc92e97c9771f1cec8f0fea73c79c
- sha512sums = 171d84c3f3076f62f1630141f85f662a543d2eb46267cc4dc2e80711cc59a0408cabc31372223993543bf621876657a519909e79e381a62a6ada0857d0473e46
- sha512sums = 3f4012212805c5ef72f6cf552ae50200afb640f5062d64795ed383918dcb8b7f9e0579dc93b17a7abe2c9d4cc579539b247c628b52a92d257f06e4d16e8f3975
- sha512sums = 6b978b8039cb05ff0dbde66ba1da4c20e89be45cd9d186b9822fabe040715259bc317b38fbc8d5c3a82a8dd1011fb807be47a4e4d76e658095f9340149c20255
- sha512sums = a4a681546716b1a1a9e55321796828828b1872bf223c7c02529a1553b8beafe2a89ffe72a38e5aa3bf457a8ce4dd2dcf9cba7a9cde28e59586a8388f1439a8a1
- sha512sums = 8ec9a4dfb877bc5619328cc74f11f96d212397acf25b267e10d51d9ee8ad9a1993c2ceb8ebe60d9aec82071a9307d467001ddcc78146a7c62030298a979d2fbe
- sha512sums = 065534de4e0a759c3f9f10982faa2cb73d6df1d7cf8b517216b8b702039757fe2274fd8c896a28bf2d3a450f41584683ddbd39e6185422def33e9c2bbf1cde9d
- sha512sums = 2ee3b47f7dd9480660fb3e50d5ea8f5487a7b1edc76982a1792c22340b61d21e52d1b25c04a62ce26e9dccaf85d695182f4473ebb92c1bd5d0b747a6c678acd2
- sha512sums = 040a051466f9181da144319c36ce01d356fc19a3c9a5ae98e505bb80f59e4338fc04891292f181734a6ea7fc98765c6ff1df972e7a834bd1bcc069be31e3fd08
- sha512sums = b3d84154da5006359149efa152ba45e5c7840c1bacb84fda2aec3a098160c5e92cf5e663bba1751efa900b33088638317c23a4aae9d20f8708ab3f241476dd1c
- sha512sums = 343509db4ff7abe4d5cfe35e08ed8ac6d951b2fb9074fc6fc44089525ba1989d6ab576c3457e4fd55583c23c0cbaee13e6ef94836c68fe7164558ad26b4cb217
- sha512sums = f554243f4f681fdefa5721870b0448e873d61e2a6cd0b33acfd032adfdaca21f1b7071f736f079cf003c32915689793737d619949c2338ad246b26cc1d66ef7b
- sha512sums = 646cc47eabec416e981e1544dc58778873f9727e7dd8903d4f577c21f5e86df5eea938b8aee23ea2bbf5ac709199eedbfde3729bc1734ad4fc28a9510edb4804
- sha512sums = 419f63f73191a950ceb1bea79c7d1a7fecab1f79e1e71d4eef5a5403a04cdb015b8d9318f28abad46f6fe0b827f8d69430f93d5e809bb6adaefae3011278d248
- sha512sums = 8708a630bf222183308bf816a7286ae0253de7ce45ace1eafe7eb12c64ebceff6ad34e96b0be9cffa3eccf0f3c39d864bd7f0e518227e35b71325b94c088b8e6
- sha512sums = ad92035b7a9ac07ab25a040d38b3a975c6c6c0ce65bb39fe1f2b8ac6208bfab066f2ae032e0736116ca61ff2c174acab794d504ae72284a109e8ce6f27790189
pkgname = betterspades
diff --git a/PKGBUILD b/PKGBUILD
index b8810345e581..4a37a39077fa 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -2,7 +2,7 @@
pkgname=betterspades
pkgver=0.1.5
-pkgrel=2
+pkgrel=3
pkgdesc="Opensource game client for Ace Of Spades 0.75 protocol"
arch=('i686' 'x86_64')
url='https://github.com/xtreme8000/betterspades/'
@@ -10,61 +10,11 @@ license=('GPL3')
depends=('hicolor-icon-theme' 'openal' 'glfw' 'enet' 'glew')
makedepends=('glfw' 'libdeflate' 'enet' 'cmake' 'unzip')
source=("${pkgname}-${pkgver}.tar.gz::https://github.com/xtreme8000/$pkgname/archive/v${pkgver}.tar.gz"
- "http://aos.party/bsresources.zip"
"$pkgname.desktop"
- "$pkgname"
- "hashtable.c"
- "hashtable.h"
- "microui.c"
- "microui.h"
- "dr_wav.h"
- "http.h"
- "ini.c"
- "ini.h"
- "libdeflate.h"
- "lodepng.cpp"
- "lodepng.h"
- "log.c"
- "log.h"
- "parson.c"
- "parson.h"
- "stb_truetype.h")
+ "$pkgname")
sha512sums=('743c66f620be0e15a4b9055ba0e1709fcec6e7152836f4c87ad6545cd2b03fc14691b523b196608c5a62e7774f272227b3b7bc436445e5205549b55cb5284ceb'
- '83651b4befe06b534299fac03de032466f20ab8010bcb0828e8f67594865726437b527334fd3f048b5ae1939ad0e4d02bf8fd5cff2e23afba7d6e644dd6f5fa7'
'45488d7196410a1bc9461de82ac28ac5335cb99babae5f8a4465539b196a4cbe9361aa1f8d7521289537aae6fdb2a8c5c2588b20f4fd5fe7c4484f9c9c8e285b'
- '5cac9e619faa2d83a3db76753b9bbea40d3c97de579701357d6d6472feb378bb4dca1ba389b132ae6187b0a15b2bc17d42fba331e0c653682827d09af0e231aa'
- '44eb971c90986186b2c532fde9c40ec4f61bc6f1c97b4424cd074c29d7b98fe43cb973dae7b89c9587fcf06c0816b93e9dddc92e97c9771f1cec8f0fea73c79c'
- '171d84c3f3076f62f1630141f85f662a543d2eb46267cc4dc2e80711cc59a0408cabc31372223993543bf621876657a519909e79e381a62a6ada0857d0473e46'
- '3f4012212805c5ef72f6cf552ae50200afb640f5062d64795ed383918dcb8b7f9e0579dc93b17a7abe2c9d4cc579539b247c628b52a92d257f06e4d16e8f3975'
- '6b978b8039cb05ff0dbde66ba1da4c20e89be45cd9d186b9822fabe040715259bc317b38fbc8d5c3a82a8dd1011fb807be47a4e4d76e658095f9340149c20255'
- 'a4a681546716b1a1a9e55321796828828b1872bf223c7c02529a1553b8beafe2a89ffe72a38e5aa3bf457a8ce4dd2dcf9cba7a9cde28e59586a8388f1439a8a1'
- '8ec9a4dfb877bc5619328cc74f11f96d212397acf25b267e10d51d9ee8ad9a1993c2ceb8ebe60d9aec82071a9307d467001ddcc78146a7c62030298a979d2fbe'
- '065534de4e0a759c3f9f10982faa2cb73d6df1d7cf8b517216b8b702039757fe2274fd8c896a28bf2d3a450f41584683ddbd39e6185422def33e9c2bbf1cde9d'
- '2ee3b47f7dd9480660fb3e50d5ea8f5487a7b1edc76982a1792c22340b61d21e52d1b25c04a62ce26e9dccaf85d695182f4473ebb92c1bd5d0b747a6c678acd2'
- '040a051466f9181da144319c36ce01d356fc19a3c9a5ae98e505bb80f59e4338fc04891292f181734a6ea7fc98765c6ff1df972e7a834bd1bcc069be31e3fd08'
- 'b3d84154da5006359149efa152ba45e5c7840c1bacb84fda2aec3a098160c5e92cf5e663bba1751efa900b33088638317c23a4aae9d20f8708ab3f241476dd1c'
- '343509db4ff7abe4d5cfe35e08ed8ac6d951b2fb9074fc6fc44089525ba1989d6ab576c3457e4fd55583c23c0cbaee13e6ef94836c68fe7164558ad26b4cb217'
- 'f554243f4f681fdefa5721870b0448e873d61e2a6cd0b33acfd032adfdaca21f1b7071f736f079cf003c32915689793737d619949c2338ad246b26cc1d66ef7b'
- '646cc47eabec416e981e1544dc58778873f9727e7dd8903d4f577c21f5e86df5eea938b8aee23ea2bbf5ac709199eedbfde3729bc1734ad4fc28a9510edb4804'
- '419f63f73191a950ceb1bea79c7d1a7fecab1f79e1e71d4eef5a5403a04cdb015b8d9318f28abad46f6fe0b827f8d69430f93d5e809bb6adaefae3011278d248'
- '8708a630bf222183308bf816a7286ae0253de7ce45ace1eafe7eb12c64ebceff6ad34e96b0be9cffa3eccf0f3c39d864bd7f0e518227e35b71325b94c088b8e6'
- 'ad92035b7a9ac07ab25a040d38b3a975c6c6c0ce65bb39fe1f2b8ac6208bfab066f2ae032e0736116ca61ff2c174acab794d504ae72284a109e8ce6f27790189')
-
-prepare() {
- cd BetterSpades-${pkgver}
- mkdir -p src/lodepng/
- cp -v $srcdir/dr_wav.h src/dr_wav.c
- cp -v $srcdir/hashtable.* src/
- cp -v $srcdir/http.h src/
- cp -v $srcdir/ini.* src/
- cp -v $srcdir/libdeflate.h deps/
- cp -v $srcdir/lodepng.cpp src/lodepng/lodepng.c
- cp -v $srcdir/lodepng.h src/lodepng/lodepng.h
- cp -v $srcdir/log.* src/
- cp -v $srcdir/microui.* src/
- cp -v $srcdir/parson.* src/
- cp -v $srcdir/stb_truetype.h src/
-}
+ '5cac9e619faa2d83a3db76753b9bbea40d3c97de579701357d6d6472feb378bb4dca1ba389b132ae6187b0a15b2bc17d42fba331e0c653682827d09af0e231aa')
build() {
cd $srcdir/BetterSpades-${pkgver}
diff --git a/dr_wav.h b/dr_wav.h
deleted file mode 100644
index dc669885cba6..000000000000
--- a/dr_wav.h
+++ /dev/null
@@ -1,8800 +0,0 @@
-/*
-WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file.
-dr_wav - v0.13.12 - 2023-08-07
-
-David Reid - mackron@gmail.com
-
-GitHub: https://github.com/mackron/dr_libs
-*/
-
-/*
-Introduction
-============
-This is a single file library. To use it, do something like the following in one .c file.
-
- ```c
- #define DR_WAV_IMPLEMENTATION
- #include "dr_wav.h"
- ```
-
-You can then #include this file in other parts of the program as you would with any other header file. Do something like the following to read audio data:
-
- ```c
- drwav wav;
- if (!drwav_init_file(&wav, "my_song.wav", NULL)) {
- // Error opening WAV file.
- }
-
- drwav_int32* pDecodedInterleavedPCMFrames = malloc(wav.totalPCMFrameCount * wav.channels * sizeof(drwav_int32));
- size_t numberOfSamplesActuallyDecoded = drwav_read_pcm_frames_s32(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames);
-
- ...
-
- drwav_uninit(&wav);
- ```
-
-If you just want to quickly open and read the audio data in a single operation you can do something like this:
-
- ```c
- unsigned int channels;
- unsigned int sampleRate;
- drwav_uint64 totalPCMFrameCount;
- float* pSampleData = drwav_open_file_and_read_pcm_frames_f32("my_song.wav", &channels, &sampleRate, &totalPCMFrameCount, NULL);
- if (pSampleData == NULL) {
- // Error opening and reading WAV file.
- }
-
- ...
-
- drwav_free(pSampleData, NULL);
- ```
-
-The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in this case), but you can still output the
-audio data in its internal format (see notes below for supported formats):
-
- ```c
- size_t framesRead = drwav_read_pcm_frames(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames);
- ```
-
-You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for a particular data format:
-
- ```c
- size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer);
- ```
-
-dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at `drwav_init_write()`,
-`drwav_init_file_write()`, etc. Use `drwav_write_pcm_frames()` to write samples, or `drwav_write_raw()` to write raw data in the "data" chunk.
-
- ```c
- drwav_data_format format;
- format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64.
- format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes.
- format.channels = 2;
- format.sampleRate = 44100;
- format.bitsPerSample = 16;
- drwav_init_file_write(&wav, "data/recording.wav", &format, NULL);
-
- ...
-
- drwav_uint64 framesWritten = drwav_write_pcm_frames(pWav, frameCount, pSamples);
- ```
-
-Note that writing to AIFF or RIFX is not supported.
-
-dr_wav has support for decoding from a number of different encapsulation formats. See below for details.
-
-
-Build Options
-=============
-#define these options before including this file.
-
-#define DR_WAV_NO_CONVERSION_API
- Disables conversion APIs such as `drwav_read_pcm_frames_f32()` and `drwav_s16_to_f32()`.
-
-#define DR_WAV_NO_STDIO
- Disables APIs that initialize a decoder from a file such as `drwav_init_file()`, `drwav_init_file_write()`, etc.
-
-#define DR_WAV_NO_WCHAR
- Disables all functions ending with `_w`. Use this if your compiler does not provide wchar.h. Not required if DR_WAV_NO_STDIO is also defined.
-
-
-Supported Encapsulations
-========================
-- RIFF (Regular WAV)
-- RIFX (Big-Endian)
-- AIFF (Does not currently support ADPCM)
-- RF64
-- W64
-
-Note that AIFF and RIFX do not support write mode, nor do they support reading of metadata.
-
-
-Supported Encodings
-===================
-- Unsigned 8-bit PCM
-- Signed 12-bit PCM
-- Signed 16-bit PCM
-- Signed 24-bit PCM
-- Signed 32-bit PCM
-- IEEE 32-bit floating point
-- IEEE 64-bit floating point
-- A-law and u-law
-- Microsoft ADPCM
-- IMA ADPCM (DVI, format code 0x11)
-
-8-bit PCM encodings are always assumed to be unsigned. Signed 8-bit encoding can only be read with `drwav_read_raw()`.
-
-Note that ADPCM is not currently supported with AIFF. Contributions welcome.
-
-
-Notes
-=====
-- Samples are always interleaved.
-- The default read function does not do any data conversion. Use `drwav_read_pcm_frames_f32()`, `drwav_read_pcm_frames_s32()` and `drwav_read_pcm_frames_s16()`
- to read and convert audio data to 32-bit floating point, signed 32-bit integer and signed 16-bit integer samples respectively.
-- dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format.
-*/
-
-#ifndef dr_wav_h
-#define dr_wav_h
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define DRWAV_STRINGIFY(x) #x
-#define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x)
-
-#define DRWAV_VERSION_MAJOR 0
-#define DRWAV_VERSION_MINOR 13
-#define DRWAV_VERSION_REVISION 12
-#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION)
-
-#include <stddef.h> /* For size_t. */
-
-/* Sized Types */
-typedef signed char drwav_int8;
-typedef unsigned char drwav_uint8;
-typedef signed short drwav_int16;
-typedef unsigned short drwav_uint16;
-typedef signed int drwav_int32;
-typedef unsigned int drwav_uint32;
-#if defined(_MSC_VER) && !defined(__clang__)
- typedef signed __int64 drwav_int64;
- typedef unsigned __int64 drwav_uint64;
-#else
- #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wlong-long"
- #if defined(__clang__)
- #pragma GCC diagnostic ignored "-Wc++11-long-long"
- #endif
- #endif
- typedef signed long long drwav_int64;
- typedef unsigned long long drwav_uint64;
- #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
- #pragma GCC diagnostic pop
- #endif
-#endif
-#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__)
- typedef drwav_uint64 drwav_uintptr;
-#else
- typedef drwav_uint32 drwav_uintptr;
-#endif
-typedef drwav_uint8 drwav_bool8;
-typedef drwav_uint32 drwav_bool32;
-#define DRWAV_TRUE 1
-#define DRWAV_FALSE 0
-/* End Sized Types */
-
-/* Decorations */
-#if !defined(DRWAV_API)
- #if defined(DRWAV_DLL)
- #if defined(_WIN32)
- #define DRWAV_DLL_IMPORT __declspec(dllimport)
- #define DRWAV_DLL_EXPORT __declspec(dllexport)
- #define DRWAV_DLL_PRIVATE static
- #else
- #if defined(__GNUC__) && __GNUC__ >= 4
- #define DRWAV_DLL_IMPORT __attribute__((visibility("default")))
- #define DRWAV_DLL_EXPORT __attribute__((visibility("default")))
- #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden")))
- #else
- #define DRWAV_DLL_IMPORT
- #define DRWAV_DLL_EXPORT
- #define DRWAV_DLL_PRIVATE static
- #endif
- #endif
-
- #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION)
- #define DRWAV_API DRWAV_DLL_EXPORT
- #else
- #define DRWAV_API DRWAV_DLL_IMPORT
- #endif
- #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE
- #else
- #define DRWAV_API extern
- #define DRWAV_PRIVATE static
- #endif
-#endif
-/* End Decorations */
-
-/* Result Codes */
-typedef drwav_int32 drwav_result;
-#define DRWAV_SUCCESS 0
-#define DRWAV_ERROR -1 /* A generic error. */
-#define DRWAV_INVALID_ARGS -2
-#define DRWAV_INVALID_OPERATION -3
-#define DRWAV_OUT_OF_MEMORY -4
-#define DRWAV_OUT_OF_RANGE -5
-#define DRWAV_ACCESS_DENIED -6
-#define DRWAV_DOES_NOT_EXIST -7
-#define DRWAV_ALREADY_EXISTS -8
-#define DRWAV_TOO_MANY_OPEN_FILES -9
-#define DRWAV_INVALID_FILE -10
-#define DRWAV_TOO_BIG -11
-#define DRWAV_PATH_TOO_LONG -12
-#define DRWAV_NAME_TOO_LONG -13
-#define DRWAV_NOT_DIRECTORY -14
-#define DRWAV_IS_DIRECTORY -15
-#define DRWAV_DIRECTORY_NOT_EMPTY -16
-#define DRWAV_END_OF_FILE -17
-#define DRWAV_NO_SPACE -18
-#define DRWAV_BUSY -19
-#define DRWAV_IO_ERROR -20
-#define DRWAV_INTERRUPT -21
-#define DRWAV_UNAVAILABLE -22
-#define DRWAV_ALREADY_IN_USE -23
-#define DRWAV_BAD_ADDRESS -24
-#define DRWAV_BAD_SEEK -25
-#define DRWAV_BAD_PIPE -26
-#define DRWAV_DEADLOCK -27
-#define DRWAV_TOO_MANY_LINKS -28
-#define DRWAV_NOT_IMPLEMENTED -29
-#define DRWAV_NO_MESSAGE -30
-#define DRWAV_BAD_MESSAGE -31
-#define DRWAV_NO_DATA_AVAILABLE -32
-#define DRWAV_INVALID_DATA -33
-#define DRWAV_TIMEOUT -34
-#define DRWAV_NO_NETWORK -35
-#define DRWAV_NOT_UNIQUE -36
-#define DRWAV_NOT_SOCKET -37
-#define DRWAV_NO_ADDRESS -38
-#define DRWAV_BAD_PROTOCOL -39
-#define DRWAV_PROTOCOL_UNAVAILABLE -40
-#define DRWAV_PROTOCOL_NOT_SUPPORTED -41
-#define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42
-#define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43
-#define DRWAV_SOCKET_NOT_SUPPORTED -44
-#define DRWAV_CONNECTION_RESET -45
-#define DRWAV_ALREADY_CONNECTED -46
-#define DRWAV_NOT_CONNECTED -47
-#define DRWAV_CONNECTION_REFUSED -48
-#define DRWAV_NO_HOST -49
-#define DRWAV_IN_PROGRESS -50
-#define DRWAV_CANCELLED -51
-#define DRWAV_MEMORY_ALREADY_MAPPED -52
-#define DRWAV_AT_END -53
-/* End Result Codes */
-
-/* Common data formats. */
-#define DR_WAVE_FORMAT_PCM 0x1
-#define DR_WAVE_FORMAT_ADPCM 0x2
-#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3
-#define DR_WAVE_FORMAT_ALAW 0x6
-#define DR_WAVE_FORMAT_MULAW 0x7
-#define DR_WAVE_FORMAT_DVI_ADPCM 0x11
-#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
-
-/* Flags to pass into drwav_init_ex(), etc. */
-#define DRWAV_SEQUENTIAL 0x00000001
-#define DRWAV_WITH_METADATA 0x00000002
-
-DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision);
-DRWAV_API const char* drwav_version_string(void);
-
-/* Allocation Callbacks */
-typedef struct
-{
- void* pUserData;
- void* (* onMalloc)(size_t sz, void* pUserData);
- void* (* onRealloc)(void* p, size_t sz, void* pUserData);
- void (* onFree)(void* p, void* pUserData);
-} drwav_allocation_callbacks;
-/* End Allocation Callbacks */
-
-typedef enum
-{
- drwav_seek_origin_start,
- drwav_seek_origin_current
-} drwav_seek_origin;
-
-typedef enum
-{
- drwav_container_riff,
- drwav_container_rifx,
- drwav_container_w64,
- drwav_container_rf64,
- drwav_container_aiff
-} drwav_container;
-
-typedef struct
-{
- union
- {
- drwav_uint8 fourcc[4];
- drwav_uint8 guid[16];
- } id;
-
- /* The size in bytes of the chunk. */
- drwav_uint64 sizeInBytes;
-
- /*
- RIFF = 2 byte alignment.
- W64 = 8 byte alignment.
- */
- unsigned int paddingSize;
-} drwav_chunk_header;
-
-typedef struct
-{
- /*
- The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications
- that require support for data formats not natively supported by dr_wav.
- */
- drwav_uint16 formatTag;
-
- /* The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc. */
- drwav_uint16 channels;
-
- /* The sample rate. Usually set to something like 44100. */
- drwav_uint32 sampleRate;
-
- /* Average bytes per second. You probably don't need this, but it's left here for informational purposes. */
- drwav_uint32 avgBytesPerSec;
-
- /* Block align. This is equal to the number of channels * bytes per sample. */
- drwav_uint16 blockAlign;
-
- /* Bits per sample. */
- drwav_uint16 bitsPerSample;
-
- /* The size of the extended data. Only used internally for validation, but left here for informational purposes. */
- drwav_uint16 extendedSize;
-
- /*
- The number of valid bits per sample. When <formatTag> is equal to WAVE_FORMAT_EXTENSIBLE, <bitsPerSample>
- is always rounded up to the nearest multiple of 8. This variable contains information about exactly how
- many bits are valid per sample. Mainly used for informational purposes.
- */
- drwav_uint16 validBitsPerSample;
-
- /* The channel mask. Not used at the moment. */
- drwav_uint32 channelMask;
-
- /* The sub-format, exactly as specified by the wave file. */
- drwav_uint8 subFormat[16];
-} drwav_fmt;
-
-DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT);
-
-
-/*
-Callback for when data is read. Return value is the number of bytes actually read.
-
-pUserData [in] The user data that was passed to drwav_init() and family.
-pBufferOut [out] The output buffer.
-bytesToRead [in] The number of bytes to read.
-
-Returns the number of bytes actually read.
-
-A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
-either the entire bytesToRead is filled or you have reached the end of the stream.
-*/
-typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
-
-/*
-Callback for when data is written. Returns value is the number of bytes actually written.
-
-pUserData [in] The user data that was passed to drwav_init_write() and family.
-pData [out] A pointer to the data to write.
-bytesToWrite [in] The number of bytes to write.
-
-Returns the number of bytes actually written.
-
-If the return value differs from bytesToWrite, it indicates an error.
-*/
-typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
-
-/*
-Callback for when data needs to be seeked.
-
-pUserData [in] The user data that was passed to drwav_init() and family.
-offset [in] The number of bytes to move, relative to the origin. Will never be negative.
-origin [in] The origin of the seek - the current position or the start of the stream.
-
-Returns whether or not the seek was successful.
-
-Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which will be either drwav_seek_origin_start or
-drwav_seek_origin_current.
-*/
-typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
-
-/*
-Callback for when drwav_init_ex() finds a chunk.
-
-pChunkUserData [in] The user data that was passed to the pChunkUserData parameter of drwav_init_ex() and family.
-onRead [in] A pointer to the function to call when reading.
-onSeek [in] A pointer to the function to call when seeking.
-pReadSeekUserData [in] The user data that was passed to the pReadSeekUserData parameter of drwav_init_ex() and family.
-pChunkHeader [in] A pointer to an object containing basic header information about the chunk. Use this to identify the chunk.
-container [in] Whether or not the WAV file is a RIFF or Wave64 container. If you're unsure of the difference, assume RIFF.
-pFMT [in] A pointer to the object containing the contents of the "fmt" chunk.
-
-Returns the number of bytes read + seeked.
-
-To read data from the chunk, call onRead(), passing in pReadSeekUserData as the first parameter. Do the same for seeking with onSeek(). The return value must
-be the total number of bytes you have read _plus_ seeked.
-
-Use the `container` argument to discriminate the fields in `pChunkHeader->id`. If the container is `drwav_container_riff` or `drwav_container_rf64` you should
-use `id.fourcc`, otherwise you should use `id.guid`.
-
-The `pFMT` parameter can be used to determine the data format of the wave file. Use `drwav_fmt_get_format()` to get the sample format, which will be one of the
-`DR_WAVE_FORMAT_*` identifiers.
-
-The read pointer will be sitting on the first byte after the chunk's header. You must not attempt to read beyond the boundary of the chunk.
-*/
-typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT);
-
-
-/* Structure for internal use. Only used for loaders opened with drwav_init_memory(). */
-typedef struct
-{
- const drwav_uint8* data;
- size_t dataSize;
- size_t currentReadPos;
-} drwav__memory_stream;
-
-/* Structure for internal use. Only used for writers opened with drwav_init_memory_write(). */
-typedef struct
-{
- void** ppData;
- size_t* pDataSize;
- size_t dataSize;
- size_t dataCapacity;
- size_t currentWritePos;
-} drwav__memory_stream_write;
-
-typedef struct
-{
- drwav_container container; /* RIFF, W64. */
- drwav_uint32 format; /* DR_WAVE_FORMAT_* */
- drwav_uint32 channels;
- drwav_uint32 sampleRate;
- drwav_uint32 bitsPerSample;
-} drwav_data_format;
-
-typedef enum
-{
- drwav_metadata_type_none = 0,
-
- /*
- Unknown simply means a chunk that drwav does not handle specifically. You can still ask to
- receive these chunks as metadata objects. It is then up to you to interpret the chunk's data.
- You can also write unknown metadata to a wav file. Be careful writing unknown chunks if you
- have also edited the audio data. The unknown chunks could represent offsets/sizes that no
- longer correctly correspond to the audio data.
- */
- drwav_metadata_type_unknown = 1 << 0,
-
- /* Only 1 of each of these metadata items are allowed in a wav file. */
- drwav_metadata_type_smpl = 1 << 1,
- drwav_metadata_type_inst = 1 << 2,
- drwav_metadata_type_cue = 1 << 3,
- drwav_metadata_type_acid = 1 << 4,
- drwav_metadata_type_bext = 1 << 5,
-
- /*
- Wav files often have a LIST chunk. This is a chunk that contains a set of subchunks. For this
- higher-level metadata API, we don't make a distinction between a regular chunk and a LIST
- subchunk. Instead, they are all just 'metadata' items.
-
- There can be multiple of these metadata items in a wav file.
- */
- drwav_metadata_type_list_label = 1 << 6,
- drwav_metadata_type_list_note = 1 << 7,
- drwav_metadata_type_list_labelled_cue_region = 1 << 8,
-
- drwav_metadata_type_list_info_software = 1 << 9,
- drwav_metadata_type_list_info_copyright = 1 << 10,
- drwav_metadata_type_list_info_title = 1 << 11,
- drwav_metadata_type_list_info_artist = 1 << 12,
- drwav_metadata_type_list_info_comment = 1 << 13,
- drwav_metadata_type_list_info_date = 1 << 14,
- drwav_metadata_type_list_info_genre = 1 << 15,
- drwav_metadata_type_list_info_album = 1 << 16,
- drwav_metadata_type_list_info_tracknumber = 1 << 17,
-
- /* Other type constants for convenience. */
- drwav_metadata_type_list_all_info_strings = drwav_metadata_type_list_info_software
- | drwav_metadata_type_list_info_copyright
- | drwav_metadata_type_list_info_title
- | drwav_metadata_type_list_info_artist
- | drwav_metadata_type_list_info_comment
- | drwav_metadata_type_list_info_date
- | drwav_metadata_type_list_info_genre
- | drwav_metadata_type_list_info_album
- | drwav_metadata_type_list_info_tracknumber,
-
- drwav_metadata_type_list_all_adtl = drwav_metadata_type_list_label
- | drwav_metadata_type_list_note
- | drwav_metadata_type_list_labelled_cue_region,
-
- drwav_metadata_type_all = -2, /*0xFFFFFFFF & ~drwav_metadata_type_unknown,*/
- drwav_metadata_type_all_including_unknown = -1 /*0xFFFFFFFF,*/
-} drwav_metadata_type;
-
-/*
-Sampler Metadata
-
-The sampler chunk contains information about how a sound should be played in the context of a whole
-audio production, and when used in a sampler. See https://en.wikipedia.org/wiki/Sample-based_synthesis.
-*/
-typedef enum
-{
- drwav_smpl_loop_type_forward = 0,
- drwav_smpl_loop_type_pingpong = 1,
- drwav_smpl_loop_type_backward = 2
-} drwav_smpl_loop_type;
-
-typedef struct
-{
- /* The ID of the associated cue point, see drwav_cue and drwav_cue_point. As with all cue point IDs, this can correspond to a label chunk to give this loop a name, see drwav_list_label_or_note. */
- drwav_uint32 cuePointId;
-
- /* See drwav_smpl_loop_type. */
- drwav_uint32 type;
-
- /* The byte offset of the first sample to be played in the loop. */
- drwav_uint32 firstSampleByteOffset;
-
- /* The byte offset into the audio data of the last sample to be played in the loop. */
- drwav_uint32 lastSampleByteOffset;
-
- /* A value to represent that playback should occur at a point between samples. This value ranges from 0 to UINT32_MAX. Where a value of 0 means no fraction, and a value of (UINT32_MAX / 2) would mean half a sample. */
- drwav_uint32 sampleFraction;
-
- /* Number of times to play the loop. 0 means loop infinitely. */
- drwav_uint32 playCount;
-} drwav_smpl_loop;
-
-typedef struct
-{
- /* IDs for a particular MIDI manufacturer. 0 if not used. */
- drwav_uint32 manufacturerId;
- drwav_uint32 productId;
-
- /* The period of 1 sample in nanoseconds. */
- drwav_uint32 samplePeriodNanoseconds;
-
- /* The MIDI root note of this file. 0 to 127. */
- drwav_uint32 midiUnityNote;
-
- /* The fraction of a semitone up from the given MIDI note. This is a value from 0 to UINT32_MAX, where 0 means no change and (UINT32_MAX / 2) is half a semitone (AKA 50 cents). */
- drwav_uint32 midiPitchFraction;
-
- /* Data relating to SMPTE standards which are used for syncing audio and video. 0 if not used. */
- drwav_uint32 smpteFormat;
- drwav_uint32 smpteOffset;
-
- /* drwav_smpl_loop loops. */
- drwav_uint32 sampleLoopCount;
-
- /* Optional sampler-specific data. */
- drwav_uint32 samplerSpecificDataSizeInBytes;
-
- drwav_smpl_loop* pLoops;
- drwav_uint8* pSamplerSpecificData;
-} drwav_smpl;
-
-/*
-Instrument Metadata
-
-The inst metadata contains data about how a sound should be played as part of an instrument. This
-commonly read by samplers. See https://en.wikipedia.org/wiki/Sample-based_synthesis.
-*/
-typedef struct
-{
- drwav_int8 midiUnityNote; /* The root note of the audio as a MIDI note number. 0 to 127. */
- drwav_int8 fineTuneCents; /* -50 to +50 */
- drwav_int8 gainDecibels; /* -64 to +64 */
- drwav_int8 lowNote; /* 0 to 127 */
- drwav_int8 highNote; /* 0 to 127 */
- drwav_int8 lowVelocity; /* 1 to 127 */
- drwav_int8 highVelocity; /* 1 to 127 */
-} drwav_inst;
-
-/*
-Cue Metadata
-
-Cue points are markers at specific points in the audio. They often come with an associated piece of
-drwav_list_label_or_note metadata which contains the text for the marker.
-*/
-typedef struct
-{
- /* Unique identification value. */
- drwav_uint32 id;
-
- /* Set to 0. This is only relevant if there is a 'playlist' chunk - which is not supported by dr_wav. */
- drwav_uint32 playOrderPosition;
-
- /* Should always be "data". This represents the fourcc value of the chunk that this cue point corresponds to. dr_wav only supports a single data chunk so this should always be "data". */
- drwav_uint8 dataChunkId[4];
-
- /* Set to 0. This is only relevant if there is a wave list chunk. dr_wav, like lots of readers/writers, do not support this. */
- drwav_uint32 chunkStart;
-
- /* Set to 0 for uncompressed formats. Else the last byte in compressed wave data where decompression can begin to find the value of the corresponding sample value. */
- drwav_uint32 blockStart;
-
- /* For uncompressed formats this is the byte offset of the cue point into the audio data. For compressed formats this is relative to the block specified with blockStart. */
- drwav_uint32 sampleByteOffset;
-} drwav_cue_point;
-
-typedef struct
-{
- drwav_uint32 cuePointCount;
- drwav_cue_point *pCuePoints;
-} drwav_cue;
-
-/*
-Acid Metadata
-
-This chunk contains some information about the time signature and the tempo of the audio.
-*/
-typedef enum
-{
- drwav_acid_flag_one_shot = 1, /* If this is not set, then it is a loop instead of a one-shot. */
- drwav_acid_flag_root_note_set = 2,
- drwav_acid_flag_stretch = 4,
- drwav_acid_flag_disk_based = 8,
- drwav_acid_flag_acidizer = 16 /* Not sure what this means. */
-} drwav_acid_flag;
-
-typedef struct
-{
- /* A bit-field, see drwav_acid_flag. */
- drwav_uint32 flags;
-
- /* Valid if flags contains drwav_acid_flag_root_note_set. It represents the MIDI root note the file - a value from 0 to 127. */
- drwav_uint16 midiUnityNote;
-
- /* Reserved values that should probably be ignored. reserved1 seems to often be 128 and reserved2 is 0. */
- drwav_uint16 reserved1;
- float reserved2;
-
- /* Number of beats. */
- drwav_uint32 numBeats;
-
- /* The time signature of the audio. */
- drwav_uint16 meterDenominator;
- drwav_uint16 meterNumerator;
-
- /* Beats per minute of the track. Setting a value of 0 suggests that there is no tempo. */
- float tempo;
-} drwav_acid;
-
-/*
-Cue Label or Note metadata
-
-These are 2 different types of metadata, but they have the exact same format. Labels tend to be the
-more common and represent a short name for a cue point. Notes might be used to represent a longer
-comment.
-*/
-typedef struct
-{
- /* The ID of a cue point that this label or note corresponds to. */
- drwav_uint32 cuePointId;
-
- /* Size of the string not including any null terminator. */
- drwav_uint32 stringLength;
-
- /* The string. The *init_with_metadata functions null terminate this for convenience. */
- char* pString;
-} drwav_list_label_or_note;
-
-/*
-BEXT metadata, also known as Broadcast Wave Format (BWF)
-
-This metadata adds some extra description to an audio file. You must check the version field to
-determine if the UMID or the loudness fields are valid.
-*/
-typedef struct
-{
- /*
- These top 3 fields, and the umid field are actually defined in the standard as a statically
- sized buffers. In order to reduce the size of this struct (and therefore the union in the
- metadata struct), we instead store these as pointers.
- */
- char* pDescription; /* Can be NULL or a null-terminated string, must be <= 256 characters. */
- char* pOriginatorName; /* Can be NULL or a null-terminated string, must be <= 32 characters. */
- char* pOriginatorReference; /* Can be NULL or a null-terminated string, must be <= 32 characters. */
- char pOriginationDate[10]; /* ASCII "yyyy:mm:dd". */
- char pOriginationTime[8]; /* ASCII "hh:mm:ss". */
- drwav_uint64 timeReference; /* First sample count since midnight. */
- drwav_uint16 version; /* Version of the BWF, check this to see if the fields below are valid. */
-
- /*
- Unrestricted ASCII characters containing a collection of strings terminated by CR/LF. Each
- string shall contain a description of a coding process applied to the audio data.
- */
- char* pCodingHistory;
- drwav_uint32 codingHistorySize;
-
- /* Fields below this point are only valid if the version is 1 or above. */
- drwav_uint8* pUMID; /* Exactly 64 bytes of SMPTE UMID */
-
- /* Fields below this point are only valid if the version is 2 or above. */
- drwav_uint16 loudnessValue; /* Integrated Loudness Value of the file in LUFS (multiplied by 100). */
- drwav_uint16 loudnessRange; /* Loudness Range of the file in LU (multiplied by 100). */
- drwav_uint16 maxTruePeakLevel; /* Maximum True Peak Level of the file expressed as dBTP (multiplied by 100). */
- drwav_uint16 maxMomentaryLoudness; /* Highest value of the Momentary Loudness Level of the file in LUFS (multiplied by 100). */
- drwav_uint16 maxShortTermLoudness; /* Highest value of the Short-Term Loudness Level of the file in LUFS (multiplied by 100). */
-} drwav_bext;
-
-/*
-Info Text Metadata
-
-There a many different types of information text that can be saved in this format. This is where
-things like the album name, the artists, the year it was produced, etc are saved. See
-drwav_metadata_type for the full list of types that dr_wav supports.
-*/
-typedef struct
-{
- /* Size of the string not including any null terminator. */
- drwav_uint32 stringLength;
-
- /* The string. The *init_with_metadata functions null terminate this for convenience. */
- char* pString;
-} drwav_list_info_text;
-
-/*
-Labelled Cue Region Metadata
-
-The labelled cue region metadata is used to associate some region of audio with text. The region
-starts at a cue point, and extends for the given number of samples.
-*/
-typedef struct
-{
- /* The ID of a cue point that this object corresponds to. */
- drwav_uint32 cuePointId;
-
- /* The number of samples from the cue point forwards that should be considered this region */
- drwav_uint32 sampleLength;
-
- /* Four characters used to say what the purpose of this region is. */
- drwav_uint8 purposeId[4];
-
- /* Unsure of the exact meanings of these. It appears to be acceptable to set them all to 0. */
- drwav_uint16 country;
- drwav_uint16 language;
- drwav_uint16 dialect;
- drwav_uint16 codePage;
-
- /* Size of the string not including any null terminator. */
- drwav_uint32 stringLength;
-
- /* The string. The *init_with_metadata functions null terminate this for convenience. */
- char* pString;
-} drwav_list_labelled_cue_region;
-
-/*
-Unknown Metadata
-
-This chunk just represents a type of chunk that dr_wav does not understand.
-
-Unknown metadata has a location attached to it. This is because wav files can have a LIST chunk
-that contains subchunks. These LIST chunks can be one of two types. An adtl list, or an INFO
-list. This enum is used to specify the location of a chunk that dr_wav currently doesn't support.
-*/
-typedef enum
-{
- drwav_metadata_location_invalid,
- drwav_metadata_location_top_level,
- drwav_metadata_location_inside_info_list,
- drwav_metadata_location_inside_adtl_list
-} drwav_metadata_location;
-
-typedef struct
-{
- drwav_uint8 id[4];
- drwav_metadata_location chunkLocation;
- drwav_uint32 dataSizeInBytes;
- drwav_uint8* pData;
-} drwav_unknown_metadata;
-
-/*
-Metadata is saved as a union of all the supported types.
-*/
-typedef struct
-{
- /* Determines which item in the union is valid. */
- drwav_metadata_type type;
-
- union
- {
- drwav_cue cue;
- drwav_smpl smpl;
- drwav_acid acid;
- drwav_inst inst;
- drwav_bext bext;
- drwav_list_label_or_note labelOrNote; /* List label or list note. */
- drwav_list_labelled_cue_region labelledCueRegion;
- drwav_list_info_text infoText; /* Any of the list info types. */
- drwav_unknown_metadata unknown;
- } data;
-} drwav_metadata;
-
-typedef struct
-{
- /* A pointer to the function to call when more data is needed. */
- drwav_read_proc onRead;
-
- /* A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode. */
- drwav_write_proc onWrite;
-
- /* A pointer to the function to call when the wav file needs to be seeked. */
- drwav_seek_proc onSeek;
-
- /* The user data to pass to callbacks. */
- void* pUserData;
-
- /* Allocation callbacks. */
- drwav_allocation_callbacks allocationCallbacks;
-
-
- /* Whether or not the WAV file is formatted as a standard RIFF file or W64. */
- drwav_container container;
-
-
- /* Structure containing format information exactly as specified by the wav file. */
- drwav_fmt fmt;
-
- /* The sample rate. Will be set to something like 44100. */
- drwav_uint32 sampleRate;
-
- /* The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. */
- drwav_uint16 channels;
-
- /* The bits per sample. Will be set to something like 16, 24, etc. */
- drwav_uint16 bitsPerSample;
-
- /* Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE). */
- drwav_uint16 translatedFormatTag;
-
- /* The total number of PCM frames making up the audio data. */
- drwav_uint64 totalPCMFrameCount;
-
-
- /* The size in bytes of the data chunk. */
- drwav_uint64 dataChunkDataSize;
-
- /* The position in the stream of the first data byte of the data chunk. This is used for seeking. */
- drwav_uint64 dataChunkDataPos;
-
- /* The number of bytes remaining in the data chunk. */
- drwav_uint64 bytesRemaining;
-
- /* The current read position in PCM frames. */
- drwav_uint64 readCursorInPCMFrames;
-
-
- /*
- Only used in sequential write mode. Keeps track of the desired size of the "data" chunk at the point of initialization time. Always
- set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation.
- */
- drwav_uint64 dataChunkDataSizeTargetWrite;
-
- /* Keeps track of whether or not the wav writer was initialized in sequential mode. */
- drwav_bool32 isSequentialWrite;
-
-
- /* A array of metadata. This is valid after the *init_with_metadata call returns. It will be valid until drwav_uninit() is called. You can take ownership of this data with drwav_take_ownership_of_metadata(). */
- drwav_metadata* pMetadata;
- drwav_uint32 metadataCount;
-
-
- /* A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_init_memory(). */
- drwav__memory_stream memoryStream;
- drwav__memory_stream_write memoryStreamWrite;
-
-
- /* Microsoft ADPCM specific data. */
- struct
- {
- drwav_uint32 bytesRemainingInBlock;
- drwav_uint16 predictor[2];
- drwav_int32 delta[2];
- drwav_int32 cachedFrames[4]; /* Samples are stored in this cache during decoding. */
- drwav_uint32 cachedFrameCount;
- drwav_int32 prevFrames[2][2]; /* The previous 2 samples for each channel (2 channels at most). */
- } msadpcm;
-
- /* IMA ADPCM specific data. */
- struct
- {
- drwav_uint32 bytesRemainingInBlock;
- drwav_int32 predictor[2];
- drwav_int32 stepIndex[2];
- drwav_int32 cachedFrames[16]; /* Samples are stored in this cache during decoding. */
- drwav_uint32 cachedFrameCount;
- } ima;
-
- /* AIFF specific data. */
- struct
- {
- drwav_bool8 isLE; /* Will be set to true if the audio data is little-endian encoded. */
- drwav_bool8 isUnsigned; /* Only used for 8-bit samples. When set to true, will be treated as unsigned. */
- } aiff;
-} drwav;
-
-
-/*
-Initializes a pre-allocated drwav object for reading.
-
-pWav [out] A pointer to the drwav object being initialized.
-onRead [in] The function to call when data needs to be read from the client.
-onSeek [in] The function to call when the read position of the client data needs to move.
-onChunk [in, optional] The function to call when a chunk is enumerated at initialized time.
-pUserData, pReadSeekUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
-pChunkUserData [in, optional] A pointer to application defined data that will be passed to onChunk.
-flags [in, optional] A set of flags for controlling how things are loaded.
-
-Returns true if successful; false otherwise.
-
-Close the loader with drwav_uninit().
-
-This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
-to open the stream from a file or from a block of memory respectively.
-
-Possible values for flags:
- DRWAV_SEQUENTIAL: Never perform a backwards seek while loading. This disables the chunk callback and will cause this function
- to return as soon as the data chunk is found. Any chunks after the data chunk will be ignored.
-
-drwav_init() is equivalent to "drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);".
-
-The onChunk callback is not called for the WAVE or FMT chunks. The contents of the FMT chunk can be read from pWav->fmt
-after the function returns.
-
-See also: drwav_init_file(), drwav_init_memory(), drwav_uninit()
-*/
-DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
-
-/*
-Initializes a pre-allocated drwav object for writing.
-
-onWrite [in] The function to call when data needs to be written.
-onSeek [in] The function to call when the write position needs to move.
-pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
-metadata, numMetadata [in, optional] An array of metadata objects that should be written to the file. The array is not edited. You are responsible for this metadata memory and it must maintain valid until drwav_uninit() is called.
-
-Returns true if successful; false otherwise.
-
-Close the writer with drwav_uninit().
-
-This is the lowest level function for initializing a WAV file. You can also use drwav_init_file_write() and drwav_init_memory_write()
-to open the stream from a file or from a block of memory respectively.
-
-If the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform
-a post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek.
-
-See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit()
-*/
-DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount);
-
-/*
-Utility function to determine the target size of the entire data to be written (including all headers and chunks).
-
-Returns the target size in bytes.
-
-The metadata argument can be NULL meaning no metadata exists.
-
-Useful if the application needs to know the size to allocate.
-
-Only writing to the RIFF chunk and one data chunk is currently supported.
-
-See also: drwav_init_write(), drwav_init_file_write(), drwav_init_memory_write()
-*/
-DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount);
-
-/*
-Take ownership of the metadata objects that were allocated via one of the init_with_metadata() function calls. The init_with_metdata functions perform a single heap allocation for this metadata.
-
-Useful if you want the data to persist beyond the lifetime of the drwav object.
-
-You must free the data returned from this function using drwav_free().
-*/
-DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav);
-
-/*
-Uninitializes the given drwav object.
-
-Use this only for objects initialized with drwav_init*() functions (drwav_init(), drwav_init_ex(), drwav_init_write(), drwav_init_write_sequential()).
-*/
-DRWAV_API drwav_result drwav_uninit(drwav* pWav);
-
-
-/*
-Reads raw audio data.
-
-This is the lowest level function for reading audio data. It simply reads the given number of
-bytes of the raw internal sample data.
-
-Consider using drwav_read_pcm_frames_s16(), drwav_read_pcm_frames_s32() or drwav_read_pcm_frames_f32() for
-reading sample data in a consistent format.
-
-pBufferOut can be NULL in which case a seek will be performed.
-
-Returns the number of bytes actually read.
-*/
-DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut);
-
-/*
-Reads up to the specified number of PCM frames from the WAV file.
-
-The output data will be in the file's internal format, converted to native-endian byte order. Use
-drwav_read_pcm_frames_s16/f32/s32() to read data in a specific format.
-
-If the return value is less than <framesToRead> it means the end of the file has been reached or
-you have requested more PCM frames than can possibly fit in the output buffer.
-
-This function will only work when sample data is of a fixed size and uncompressed. If you are
-using a compressed format consider using drwav_read_raw() or drwav_read_pcm_frames_s16/s32/f32().
-
-pBufferOut can be NULL in which case a seek will be performed.
-*/
-DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
-
-/*
-Seeks to the given PCM frame.
-
-Returns true if successful; false otherwise.
-*/
-DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex);
-
-/*
-Retrieves the current read position in pcm frames.
-*/
-DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor);
-
-/*
-Retrieves the length of the file.
-*/
-DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength);
-
-
-/*
-Writes raw audio data.
-
-Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error.
-*/
-DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData);
-
-/*
-Writes PCM frames.
-
-Returns the number of PCM frames written.
-
-Input samples need to be in native-endian byte order. On big-endian architectures the input data will be converted to
-little-endian. Use drwav_write_raw() to write raw audio data without performing any conversion.
-*/
-DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
-DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
-DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
-
-/* Conversion Utilities */
-#ifndef DR_WAV_NO_CONVERSION_API
-
-/*
-Reads a chunk of audio data and converts it to signed 16-bit PCM samples.
-
-pBufferOut can be NULL in which case a seek will be performed.
-
-Returns the number of PCM frames actually read.
-
-If the return value is less than <framesToRead> it means the end of the file has been reached.
-*/
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
-
-/* Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples. */
-DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
-
-/* Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples. */
-DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
-
-/* Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples. */
-DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount);
-
-/* Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples. */
-DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount);
-
-/* Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples. */
-DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount);
-
-/* Low-level function for converting A-law samples to signed 16-bit PCM samples. */
-DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
-
-/* Low-level function for converting u-law samples to signed 16-bit PCM samples. */
-DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
-
-
-/*
-Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples.
-
-pBufferOut can be NULL in which case a seek will be performed.
-
-Returns the number of PCM frames actually read.
-
-If the return value is less than <framesToRead> it means the end of the file has been reached.
-*/
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
-
-/* Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples. */
-DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
-
-/* Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples. */
-DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount);
-
-/* Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples. */
-DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
-
-/* Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples. */
-DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount);
-
-/* Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples. */
-DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
-
-/* Low-level function for converting A-law samples to IEEE 32-bit floating point samples. */
-DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
-
-/* Low-level function for converting u-law samples to IEEE 32-bit floating point samples. */
-DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
-
-
-/*
-Reads a chunk of audio data and converts it to signed 32-bit PCM samples.
-
-pBufferOut can be NULL in which case a seek will be performed.
-
-Returns the number of PCM frames actually read.
-
-If the return value is less than <framesToRead> it means the end of the file has been reached.
-*/
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
-
-/* Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples. */
-DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
-
-/* Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples. */
-DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount);
-
-/* Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples. */
-DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
-
-/* Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples. */
-DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount);
-
-/* Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples. */
-DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount);
-
-/* Low-level function for converting A-law samples to signed 32-bit PCM samples. */
-DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
-
-/* Low-level function for converting u-law samples to signed 32-bit PCM samples. */
-DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
-
-#endif /* DR_WAV_NO_CONVERSION_API */
-
-
-/* High-Level Convenience Helpers */
-
-#ifndef DR_WAV_NO_STDIO
-/*
-Helper for initializing a wave file for reading using stdio.
-
-This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
-objects because the operating system may restrict the number of file handles an application can have open at
-any given time.
-*/
-DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
-
-
-/*
-Helper for initializing a wave file for writing using stdio.
-
-This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
-objects because the operating system may restrict the number of file handles an application can have open at
-any given time.
-*/
-DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
-#endif /* DR_WAV_NO_STDIO */
-
-/*
-Helper for initializing a loader from a pre-allocated memory buffer.
-
-This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
-the lifetime of the drwav object.
-
-The buffer should contain the contents of the entire wave file, not just the sample data.
-*/
-DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
-
-/*
-Helper for initializing a writer which outputs data to a memory buffer.
-
-dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
-
-The buffer will remain allocated even after drwav_uninit() is called. The buffer should not be considered valid
-until after drwav_uninit() has been called.
-*/
-DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
-
-
-#ifndef DR_WAV_NO_CONVERSION_API
-/*
-Opens and reads an entire wav file in a single operation.
-
-The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
-*/
-DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-#ifndef DR_WAV_NO_STDIO
-/*
-Opens and decodes an entire wav file in a single operation.
-
-The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
-*/
-DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-#endif
-/*
-Opens and decodes an entire wav file from a block of memory in a single operation.
-
-The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
-*/
-DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
-#endif
-
-/* Frees data that was allocated internally by dr_wav. */
-DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks);
-
-/* Converts bytes from a wav stream to a sized type of native endian. */
-DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data);
-DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data);
-DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data);
-DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data);
-DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data);
-DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data);
-DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data);
-
-/* Compares a GUID for the purpose of checking the type of a Wave64 chunk. */
-DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]);
-
-/* Compares a four-character-code for the purpose of checking the type of a RIFF chunk. */
-DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b);
-
-#ifdef __cplusplus
-}
-#endif
-#endif /* dr_wav_h */
-
-
-/************************************************************************************************************************************************************
- ************************************************************************************************************************************************************
-
- IMPLEMENTATION
-
- ************************************************************************************************************************************************************
- ************************************************************************************************************************************************************/
-#if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION)
-#ifndef dr_wav_c
-#define dr_wav_c
-
-#ifdef __MRC__
-/* MrC currently doesn't compile dr_wav correctly with any optimizations enabled. */
-#pragma options opt off
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h> /* For INT_MAX */
-
-#ifndef DR_WAV_NO_STDIO
-#include <stdio.h>
-#ifndef DR_WAV_NO_WCHAR
-#include <wchar.h>
-#endif
-#endif
-
-/* Standard library stuff. */
-#ifndef DRWAV_ASSERT
-#include <assert.h>
-#define DRWAV_ASSERT(expression) assert(expression)
-#endif
-#ifndef DRWAV_MALLOC
-#define DRWAV_MALLOC(sz) malloc((sz))
-#endif
-#ifndef DRWAV_REALLOC
-#define DRWAV_REALLOC(p, sz) realloc((p), (sz))
-#endif
-#ifndef DRWAV_FREE
-#define DRWAV_FREE(p) free((p))
-#endif
-#ifndef DRWAV_COPY_MEMORY
-#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
-#endif
-#ifndef DRWAV_ZERO_MEMORY
-#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
-#endif
-#ifndef DRWAV_ZERO_OBJECT
-#define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p))
-#endif
-
-#define drwav_countof(x) (sizeof(x) / sizeof(x[0]))
-#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
-#define drwav_min(a, b) (((a) < (b)) ? (a) : (b))
-#define drwav_max(a, b) (((a) > (b)) ? (a) : (b))
-#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x))))
-#define drwav_offset_ptr(p, offset) (((drwav_uint8*)(p)) + (offset))
-
-#define DRWAV_MAX_SIMD_VECTOR_SIZE 32
-
-/* Architecture Detection */
-#if defined(__x86_64__) || defined(_M_X64)
- #define DRWAV_X64
-#elif defined(__i386) || defined(_M_IX86)
- #define DRWAV_X86
-#elif defined(__arm__) || defined(_M_ARM)
- #define DRWAV_ARM
-#endif
-/* End Architecture Detection */
-
-/* Inline */
-#ifdef _MSC_VER
- #define DRWAV_INLINE __forceinline
-#elif defined(__GNUC__)
- /*
- I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
- the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
- case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
- command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
- I am using "__inline__" only when we're compiling in strict ANSI mode.
- */
- #if defined(__STRICT_ANSI__)
- #define DRWAV_GNUC_INLINE_HINT __inline__
- #else
- #define DRWAV_GNUC_INLINE_HINT inline
- #endif
-
- #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
- #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT __attribute__((always_inline))
- #else
- #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT
- #endif
-#elif defined(__WATCOMC__)
- #define DRWAV_INLINE __inline
-#else
- #define DRWAV_INLINE
-#endif
-/* End Inline */
-
-/* SIZE_MAX */
-#if defined(SIZE_MAX)
- #define DRWAV_SIZE_MAX SIZE_MAX
-#else
- #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
- #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF)
- #else
- #define DRWAV_SIZE_MAX 0xFFFFFFFF
- #endif
-#endif
-/* End SIZE_MAX */
-
-/* Weird bit manipulation is for C89 compatibility (no direct support for 64-bit integers). */
-#define DRWAV_INT64_MIN ((drwav_int64)0x80000000 << 32)
-#define DRWAV_INT64_MAX ((((drwav_int64)0x7FFFFFFF) << 32) | 0xFFFFFFFF)
-
-#if defined(_MSC_VER) && _MSC_VER >= 1400
- #define DRWAV_HAS_BYTESWAP16_INTRINSIC
- #define DRWAV_HAS_BYTESWAP32_INTRINSIC
- #define DRWAV_HAS_BYTESWAP64_INTRINSIC
-#elif defined(__clang__)
- #if defined(__has_builtin)
- #if __has_builtin(__builtin_bswap16)
- #define DRWAV_HAS_BYTESWAP16_INTRINSIC
- #endif
- #if __has_builtin(__builtin_bswap32)
- #define DRWAV_HAS_BYTESWAP32_INTRINSIC
- #endif
- #if __has_builtin(__builtin_bswap64)
- #define DRWAV_HAS_BYTESWAP64_INTRINSIC
- #endif
- #endif
-#elif defined(__GNUC__)
- #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
- #define DRWAV_HAS_BYTESWAP32_INTRINSIC
- #define DRWAV_HAS_BYTESWAP64_INTRINSIC
- #endif
- #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
- #define DRWAV_HAS_BYTESWAP16_INTRINSIC
- #endif
-#endif
-
-DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision)
-{
- if (pMajor) {
- *pMajor = DRWAV_VERSION_MAJOR;
- }
-
- if (pMinor) {
- *pMinor = DRWAV_VERSION_MINOR;
- }
-
- if (pRevision) {
- *pRevision = DRWAV_VERSION_REVISION;
- }
-}
-
-DRWAV_API const char* drwav_version_string(void)
-{
- return DRWAV_VERSION_STRING;
-}
-
-/*
-These limits are used for basic validation when initializing the decoder. If you exceed these limits, first of all: what on Earth are
-you doing?! (Let me know, I'd be curious!) Second, you can adjust these by #define-ing them before the dr_wav implementation.
-*/
-#ifndef DRWAV_MAX_SAMPLE_RATE
-#define DRWAV_MAX_SAMPLE_RATE 384000
-#endif
-#ifndef DRWAV_MAX_CHANNELS
-#define DRWAV_MAX_CHANNELS 256
-#endif
-#ifndef DRWAV_MAX_BITS_PER_SAMPLE
-#define DRWAV_MAX_BITS_PER_SAMPLE 64
-#endif
-
-static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; /* 66666972-912E-11CF-A5D6-28DB04C10000 */
-static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 65766177-ACF3-11D3-8CD1-00C04F8EDB8A */
-/*static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};*/ /* 6B6E756A-ACF3-11D3-8CD1-00C04F8EDB8A */
-static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */
-static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 74636166-ACF3-11D3-8CD1-00C04F8EDB8A */
-static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 61746164-ACF3-11D3-8CD1-00C04F8EDB8A */
-/*static const drwav_uint8 drwavGUID_W64_SMPL[16] = {0x73,0x6D,0x70,0x6C, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};*/ /* 6C706D73-ACF3-11D3-8CD1-00C04F8EDB8A */
-
-
-static DRWAV_INLINE int drwav__is_little_endian(void)
-{
-#if defined(DRWAV_X86) || defined(DRWAV_X64)
- return DRWAV_TRUE;
-#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
- return DRWAV_TRUE;
-#else
- int n = 1;
- return (*(char*)&n) == 1;
-#endif
-}
-
-
-static DRWAV_INLINE void drwav_bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid)
-{
- int i;
- for (i = 0; i < 16; ++i) {
- guid[i] = data[i];
- }
-}
-
-
-static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n)
-{
-#ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC
- #if defined(_MSC_VER)
- return _byteswap_ushort(n);
- #elif defined(__GNUC__) || defined(__clang__)
- return __builtin_bswap16(n);
- #else
- #error "This compiler does not support the byte swap intrinsic."
- #endif
-#else
- return ((n & 0xFF00) >> 8) |
- ((n & 0x00FF) << 8);
-#endif
-}
-
-static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n)
-{
-#ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC
- #if defined(_MSC_VER)
- return _byteswap_ulong(n);
- #elif defined(__GNUC__) || defined(__clang__)
- #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */
- /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */
- drwav_uint32 r;
- __asm__ __volatile__ (
- #if defined(DRWAV_64BIT)
- "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */
- #else
- "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
- #endif
- );
- return r;
- #else
- return __builtin_bswap32(n);
- #endif
- #else
- #error "This compiler does not support the byte swap intrinsic."
- #endif
-#else
- return ((n & 0xFF000000) >> 24) |
- ((n & 0x00FF0000) >> 8) |
- ((n & 0x0000FF00) << 8) |
- ((n & 0x000000FF) << 24);
-#endif
-}
-
-static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n)
-{
-#ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC
- #if defined(_MSC_VER)
- return _byteswap_uint64(n);
- #elif defined(__GNUC__) || defined(__clang__)
- return __builtin_bswap64(n);
- #else
- #error "This compiler does not support the byte swap intrinsic."
- #endif
-#else
- /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */
- return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) |
- ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) |
- ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) |
- ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) |
- ((n & ((drwav_uint64)0xFF000000 )) << 8) |
- ((n & ((drwav_uint64)0x00FF0000 )) << 24) |
- ((n & ((drwav_uint64)0x0000FF00 )) << 40) |
- ((n & ((drwav_uint64)0x000000FF )) << 56);
-#endif
-}
-
-
-static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n)
-{
- return (drwav_int16)drwav__bswap16((drwav_uint16)n);
-}
-
-static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount)
-{
- drwav_uint64 iSample;
- for (iSample = 0; iSample < sampleCount; iSample += 1) {
- pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]);
- }
-}
-
-
-static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p)
-{
- drwav_uint8 t;
- t = p[0];
- p[0] = p[2];
- p[2] = t;
-}
-
-static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount)
-{
- drwav_uint64 iSample;
- for (iSample = 0; iSample < sampleCount; iSample += 1) {
- drwav_uint8* pSample = pSamples + (iSample*3);
- drwav__bswap_s24(pSample);
- }
-}
-
-
-static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n)
-{
- return (drwav_int32)drwav__bswap32((drwav_uint32)n);
-}
-
-static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount)
-{
- drwav_uint64 iSample;
- for (iSample = 0; iSample < sampleCount; iSample += 1) {
- pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]);
- }
-}
-
-
-static DRWAV_INLINE drwav_int64 drwav__bswap_s64(drwav_int64 n)
-{
- return (drwav_int64)drwav__bswap64((drwav_uint64)n);
-}
-
-static DRWAV_INLINE void drwav__bswap_samples_s64(drwav_int64* pSamples, drwav_uint64 sampleCount)
-{
- drwav_uint64 iSample;
- for (iSample = 0; iSample < sampleCount; iSample += 1) {
- pSamples[iSample] = drwav__bswap_s64(pSamples[iSample]);
- }
-}
-
-
-static DRWAV_INLINE float drwav__bswap_f32(float n)
-{
- union {
- drwav_uint32 i;
- float f;
- } x;
- x.f = n;
- x.i = drwav__bswap32(x.i);
-
- return x.f;
-}
-
-static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount)
-{
- drwav_uint64 iSample;
- for (iSample = 0; iSample < sampleCount; iSample += 1) {
- pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]);
- }
-}
-
-
-static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)
-{
- switch (bytesPerSample)
- {
- case 1:
- {
- /* No-op. */
- } break;
- case 2:
- {
- drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);
- } break;
- case 3:
- {
- drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount);
- } break;
- case 4:
- {
- drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount);
- } break;
- case 8:
- {
- drwav__bswap_samples_s64((drwav_int64*)pSamples, sampleCount);
- } break;
- default:
- {
- /* Unsupported format. */
- DRWAV_ASSERT(DRWAV_FALSE);
- } break;
- }
-}
-
-
-
-DRWAV_PRIVATE DRWAV_INLINE drwav_bool32 drwav_is_container_be(drwav_container container)
-{
- if (container == drwav_container_rifx || container == drwav_container_aiff) {
- return DRWAV_TRUE;
- } else {
- return DRWAV_FALSE;
- }
-}
-
-
-DRWAV_PRIVATE DRWAV_INLINE drwav_uint16 drwav_bytes_to_u16_le(const drwav_uint8* data)
-{
- return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8);
-}
-
-DRWAV_PRIVATE DRWAV_INLINE drwav_uint16 drwav_bytes_to_u16_be(const drwav_uint8* data)
-{
- return ((drwav_uint16)data[1] << 0) | ((drwav_uint16)data[0] << 8);
-}
-
-DRWAV_PRIVATE DRWAV_INLINE drwav_uint16 drwav_bytes_to_u16_ex(const drwav_uint8* data, drwav_container container)
-{
- if (drwav_is_container_be(container)) {
- return drwav_bytes_to_u16_be(data);
- } else {
- return drwav_bytes_to_u16_le(data);
- }
-}
-
-
-DRWAV_PRIVATE DRWAV_INLINE drwav_uint32 drwav_bytes_to_u32_le(const drwav_uint8* data)
-{
- return ((drwav_uint32)data[0] << 0) | ((drwav_uint32)data[1] << 8) | ((drwav_uint32)data[2] << 16) | ((drwav_uint32)data[3] << 24);
-}
-
-DRWAV_PRIVATE DRWAV_INLINE drwav_uint32 drwav_bytes_to_u32_be(const drwav_uint8* data)
-{
- return ((drwav_uint32)data[3] << 0) | ((drwav_uint32)data[2] << 8) | ((drwav_uint32)data[1] << 16) | ((drwav_uint32)data[0] << 24);
-}
-
-DRWAV_PRIVATE DRWAV_INLINE drwav_uint32 drwav_bytes_to_u32_ex(const drwav_uint8* data, drwav_container container)
-{
- if (drwav_is_container_be(container)) {
- return drwav_bytes_to_u32_be(data);
- } else {
- return drwav_bytes_to_u32_le(data);
- }
-}
-
-
-
-DRWAV_PRIVATE drwav_int64 drwav_aiff_extented_to_s64(const drwav_uint8* data)
-{
- drwav_uint32 exponent = ((drwav_uint32)data[0] << 8) | data[1];
- drwav_uint64 hi = ((drwav_uint64)data[2] << 24) | ((drwav_uint64)data[3] << 16) | ((drwav_uint64)data[4] << 8) | ((drwav_uint64)data[5] << 0);
- drwav_uint64 lo = ((drwav_uint64)data[6] << 24) | ((drwav_uint64)data[7] << 16) | ((drwav_uint64)data[8] << 8) | ((drwav_uint64)data[9] << 0);
- drwav_uint64 significand = (hi << 32) | lo;
- int sign = exponent >> 15;
-
- /* Remove sign bit. */
- exponent &= 0x7FFF;
-
- /* Special cases. */
- if (exponent == 0 && significand == 0) {
- return 0;
- } else if (exponent == 0x7FFF) {
- return sign ? DRWAV_INT64_MIN : DRWAV_INT64_MAX; /* Infinite. */
- }
-
- exponent -= 16383;
-
- if (exponent > 63) {
- return sign ? DRWAV_INT64_MIN : DRWAV_INT64_MAX; /* Too bit for a 64-bit integer. */
- } else if (exponent < 1) {
- return 0; /* Number is less than 1, so rounds down to 0. */
- }
-
- significand >>= (63 - exponent);
-
- if (sign) {
- return -(drwav_int64)significand;
- } else {
- return (drwav_int64)significand;
- }
-}
-
-
-DRWAV_PRIVATE void* drwav__malloc_default(size_t sz, void* pUserData)
-{
- (void)pUserData;
- return DRWAV_MALLOC(sz);
-}
-
-DRWAV_PRIVATE void* drwav__realloc_default(void* p, size_t sz, void* pUserData)
-{
- (void)pUserData;
- return DRWAV_REALLOC(p, sz);
-}
-
-DRWAV_PRIVATE void drwav__free_default(void* p, void* pUserData)
-{
- (void)pUserData;
- DRWAV_FREE(p);
-}
-
-
-DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (pAllocationCallbacks == NULL) {
- return NULL;
- }
-
- if (pAllocationCallbacks->onMalloc != NULL) {
- return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
- }
-
- /* Try using realloc(). */
- if (pAllocationCallbacks->onRealloc != NULL) {
- return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
- }
-
- return NULL;
-}
-
-DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (pAllocationCallbacks == NULL) {
- return NULL;
- }
-
- if (pAllocationCallbacks->onRealloc != NULL) {
- return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
- }
-
- /* Try emulating realloc() in terms of malloc()/free(). */
- if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
- void* p2;
-
- p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
- if (p2 == NULL) {
- return NULL;
- }
-
- if (p != NULL) {
- DRWAV_COPY_MEMORY(p2, p, szOld);
- pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
- }
-
- return p2;
- }
-
- return NULL;
-}
-
-DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (p == NULL || pAllocationCallbacks == NULL) {
- return;
- }
-
- if (pAllocationCallbacks->onFree != NULL) {
- pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
- }
-}
-
-
-DRWAV_PRIVATE drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (pAllocationCallbacks != NULL) {
- /* Copy. */
- return *pAllocationCallbacks;
- } else {
- /* Defaults. */
- drwav_allocation_callbacks allocationCallbacks;
- allocationCallbacks.pUserData = NULL;
- allocationCallbacks.onMalloc = drwav__malloc_default;
- allocationCallbacks.onRealloc = drwav__realloc_default;
- allocationCallbacks.onFree = drwav__free_default;
- return allocationCallbacks;
- }
-}
-
-
-static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag)
-{
- return
- formatTag == DR_WAVE_FORMAT_ADPCM ||
- formatTag == DR_WAVE_FORMAT_DVI_ADPCM;
-}
-
-DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize)
-{
- return (unsigned int)(chunkSize % 2);
-}
-
-DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize)
-{
- return (unsigned int)(chunkSize % 8);
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
-DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
-
-DRWAV_PRIVATE drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut)
-{
- if (container == drwav_container_riff || container == drwav_container_rifx || container == drwav_container_rf64 || container == drwav_container_aiff) {
- drwav_uint8 sizeInBytes[4];
-
- if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
- return DRWAV_AT_END;
- }
-
- if (onRead(pUserData, sizeInBytes, 4) != 4) {
- return DRWAV_INVALID_FILE;
- }
-
- pHeaderOut->sizeInBytes = drwav_bytes_to_u32_ex(sizeInBytes, container);
- pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes);
-
- *pRunningBytesReadOut += 8;
- } else if (container == drwav_container_w64) {
- drwav_uint8 sizeInBytes[8];
-
- if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
- return DRWAV_AT_END;
- }
-
- if (onRead(pUserData, sizeInBytes, 8) != 8) {
- return DRWAV_INVALID_FILE;
- }
-
- pHeaderOut->sizeInBytes = drwav_bytes_to_u64(sizeInBytes) - 24; /* <-- Subtract 24 because w64 includes the size of the header. */
- pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes);
- *pRunningBytesReadOut += 24;
- } else {
- return DRWAV_INVALID_FILE;
- }
-
- return DRWAV_SUCCESS;
-}
-
-DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
-{
- drwav_uint64 bytesRemainingToSeek = offset;
- while (bytesRemainingToSeek > 0) {
- if (bytesRemainingToSeek > 0x7FFFFFFF) {
- if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
- return DRWAV_FALSE;
- }
- bytesRemainingToSeek -= 0x7FFFFFFF;
- } else {
- if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) {
- return DRWAV_FALSE;
- }
- bytesRemainingToSeek = 0;
- }
- }
-
- return DRWAV_TRUE;
-}
-
-DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
-{
- if (offset <= 0x7FFFFFFF) {
- return onSeek(pUserData, (int)offset, drwav_seek_origin_start);
- }
-
- /* Larger than 32-bit seek. */
- if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) {
- return DRWAV_FALSE;
- }
- offset -= 0x7FFFFFFF;
-
- for (;;) {
- if (offset <= 0x7FFFFFFF) {
- return onSeek(pUserData, (int)offset, drwav_seek_origin_current);
- }
-
- if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
- return DRWAV_FALSE;
- }
- offset -= 0x7FFFFFFF;
- }
-
- /* Should never get here. */
- /*return DRWAV_TRUE; */
-}
-
-
-
-DRWAV_PRIVATE size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
-{
- size_t bytesRead;
-
- DRWAV_ASSERT(onRead != NULL);
- DRWAV_ASSERT(pCursor != NULL);
-
- bytesRead = onRead(pUserData, pBufferOut, bytesToRead);
- *pCursor += bytesRead;
- return bytesRead;
-}
-
-#if 0
-DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor)
-{
- DRWAV_ASSERT(onSeek != NULL);
- DRWAV_ASSERT(pCursor != NULL);
-
- if (!onSeek(pUserData, offset, origin)) {
- return DRWAV_FALSE;
- }
-
- if (origin == drwav_seek_origin_start) {
- *pCursor = offset;
- } else {
- *pCursor += offset;
- }
-
- return DRWAV_TRUE;
-}
-#endif
-
-
-#define DRWAV_SMPL_BYTES 36
-#define DRWAV_SMPL_LOOP_BYTES 24
-#define DRWAV_INST_BYTES 7
-#define DRWAV_ACID_BYTES 24
-#define DRWAV_CUE_BYTES 4
-#define DRWAV_BEXT_BYTES 602
-#define DRWAV_BEXT_DESCRIPTION_BYTES 256
-#define DRWAV_BEXT_ORIGINATOR_NAME_BYTES 32
-#define DRWAV_BEXT_ORIGINATOR_REF_BYTES 32
-#define DRWAV_BEXT_RESERVED_BYTES 180
-#define DRWAV_BEXT_UMID_BYTES 64
-#define DRWAV_CUE_POINT_BYTES 24
-#define DRWAV_LIST_LABEL_OR_NOTE_BYTES 4
-#define DRWAV_LIST_LABELLED_TEXT_BYTES 20
-
-#define DRWAV_METADATA_ALIGNMENT 8
-
-typedef enum
-{
- drwav__metadata_parser_stage_count,
- drwav__metadata_parser_stage_read
-} drwav__metadata_parser_stage;
-
-typedef struct
-{
- drwav_read_proc onRead;
- drwav_seek_proc onSeek;
- void *pReadSeekUserData;
- drwav__metadata_parser_stage stage;
- drwav_metadata *pMetadata;
- drwav_uint32 metadataCount;
- drwav_uint8 *pData;
- drwav_uint8 *pDataCursor;
- drwav_uint64 metadataCursor;
- drwav_uint64 extraCapacity;
-} drwav__metadata_parser;
-
-DRWAV_PRIVATE size_t drwav__metadata_memory_capacity(drwav__metadata_parser* pParser)
-{
- drwav_uint64 cap = sizeof(drwav_metadata) * (drwav_uint64)pParser->metadataCount + pParser->extraCapacity;
- if (cap > DRWAV_SIZE_MAX) {
- return 0; /* Too big. */
- }
-
- return (size_t)cap; /* Safe cast thanks to the check above. */
-}
-
-DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pParser, size_t size, size_t align)
-{
- drwav_uint8* pResult;
-
- if (align) {
- drwav_uintptr modulo = (drwav_uintptr)pParser->pDataCursor % align;
- if (modulo != 0) {
- pParser->pDataCursor += align - modulo;
- }
- }
-
- pResult = pParser->pDataCursor;
-
- /*
- Getting to the point where this function is called means there should always be memory
- available. Out of memory checks should have been done at an earlier stage.
- */
- DRWAV_ASSERT((pResult + size) <= (pParser->pData + drwav__metadata_memory_capacity(pParser)));
-
- pParser->pDataCursor += size;
- return pResult;
-}
-
-DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metadata_parser* pParser, size_t bytes, size_t align)
-{
- size_t extra = bytes + (align ? (align - 1) : 0);
- pParser->extraCapacity += extra;
-}
-
-DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) {
- pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData);
-
- pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData);
- pParser->pDataCursor = pParser->pData;
-
- if (pParser->pData == NULL) {
- return DRWAV_OUT_OF_MEMORY;
- }
-
- /*
- We don't need to worry about specifying an alignment here because malloc always returns something
- of suitable alignment. This also means pParser->pMetadata is all that we need to store in order
- for us to free when we are done.
- */
- pParser->pMetadata = (drwav_metadata*)drwav__metadata_get_memory(pParser, sizeof(drwav_metadata) * pParser->metadataCount, 1);
- pParser->metadataCursor = 0;
- }
-
- return DRWAV_SUCCESS;
-}
-
-DRWAV_PRIVATE size_t drwav__metadata_parser_read(drwav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
-{
- if (pCursor != NULL) {
- return drwav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor);
- } else {
- return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead);
- }
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata)
-{
- drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES];
- drwav_uint64 totalBytesRead = 0;
- size_t bytesJustRead;
-
- if (pMetadata == NULL) {
- return 0;
- }
-
- bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead);
-
- DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
- DRWAV_ASSERT(pChunkHeader != NULL);
-
- if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) {
- drwav_uint32 iSampleLoop;
-
- pMetadata->type = drwav_metadata_type_smpl;
- pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0);
- pMetadata->data.smpl.productId = drwav_bytes_to_u32(smplHeaderData + 4);
- pMetadata->data.smpl.samplePeriodNanoseconds = drwav_bytes_to_u32(smplHeaderData + 8);
- pMetadata->data.smpl.midiUnityNote = drwav_bytes_to_u32(smplHeaderData + 12);
- pMetadata->data.smpl.midiPitchFraction = drwav_bytes_to_u32(smplHeaderData + 16);
- pMetadata->data.smpl.smpteFormat = drwav_bytes_to_u32(smplHeaderData + 20);
- pMetadata->data.smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData + 24);
- pMetadata->data.smpl.sampleLoopCount = drwav_bytes_to_u32(smplHeaderData + 28);
- pMetadata->data.smpl.samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(smplHeaderData + 32);
-
- /*
- The loop count needs to be validated against the size of the chunk for safety so we don't
- attempt to read over the boundary of the chunk.
- */
- if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) {
- pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT);
-
- for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) {
- drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES];
- bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead);
-
- if (bytesJustRead == sizeof(smplLoopData)) {
- pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0);
- pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4);
- pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8);
- pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12);
- pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16);
- pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20);
- } else {
- break;
- }
- }
-
- if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
- pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1);
- DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL);
-
- drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead);
- }
- }
- }
-
- return totalBytesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata)
-{
- drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES];
- drwav_uint64 totalBytesRead = 0;
- size_t bytesJustRead;
-
- if (pMetadata == NULL) {
- return 0;
- }
-
- bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead);
-
- DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
-
- if (bytesJustRead == sizeof(cueHeaderSectionData)) {
- pMetadata->type = drwav_metadata_type_cue;
- pMetadata->data.cue.cuePointCount = drwav_bytes_to_u32(cueHeaderSectionData);
-
- /*
- We need to validate the cue point count against the size of the chunk so we don't read
- beyond the chunk.
- */
- if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES) {
- pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT);
- DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL);
-
- if (pMetadata->data.cue.cuePointCount > 0) {
- drwav_uint32 iCuePoint;
-
- for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
- drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES];
- bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead);
-
- if (bytesJustRead == sizeof(cuePointData)) {
- pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0);
- pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4);
- pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8];
- pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9];
- pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10];
- pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11];
- pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12);
- pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16);
- pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20);
- } else {
- break;
- }
- }
- }
- }
- }
-
- return totalBytesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata)
-{
- drwav_uint8 instData[DRWAV_INST_BYTES];
- drwav_uint64 bytesRead;
-
- if (pMetadata == NULL) {
- return 0;
- }
-
- bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL);
-
- DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
-
- if (bytesRead == sizeof(instData)) {
- pMetadata->type = drwav_metadata_type_inst;
- pMetadata->data.inst.midiUnityNote = (drwav_int8)instData[0];
- pMetadata->data.inst.fineTuneCents = (drwav_int8)instData[1];
- pMetadata->data.inst.gainDecibels = (drwav_int8)instData[2];
- pMetadata->data.inst.lowNote = (drwav_int8)instData[3];
- pMetadata->data.inst.highNote = (drwav_int8)instData[4];
- pMetadata->data.inst.lowVelocity = (drwav_int8)instData[5];
- pMetadata->data.inst.highVelocity = (drwav_int8)instData[6];
- }
-
- return bytesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata)
-{
- drwav_uint8 acidData[DRWAV_ACID_BYTES];
- drwav_uint64 bytesRead;
-
- if (pMetadata == NULL) {
- return 0;
- }
-
- bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL);
-
- DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
-
- if (bytesRead == sizeof(acidData)) {
- pMetadata->type = drwav_metadata_type_acid;
- pMetadata->data.acid.flags = drwav_bytes_to_u32(acidData + 0);
- pMetadata->data.acid.midiUnityNote = drwav_bytes_to_u16(acidData + 4);
- pMetadata->data.acid.reserved1 = drwav_bytes_to_u16(acidData + 6);
- pMetadata->data.acid.reserved2 = drwav_bytes_to_f32(acidData + 8);
- pMetadata->data.acid.numBeats = drwav_bytes_to_u32(acidData + 12);
- pMetadata->data.acid.meterDenominator = drwav_bytes_to_u16(acidData + 16);
- pMetadata->data.acid.meterNumerator = drwav_bytes_to_u16(acidData + 18);
- pMetadata->data.acid.tempo = drwav_bytes_to_f32(acidData + 20);
- }
-
- return bytesRead;
-}
-
-DRWAV_PRIVATE size_t drwav__strlen(const char* str)
-{
- size_t result = 0;
-
- while (*str++) {
- result += 1;
- }
-
- return result;
-}
-
-DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead)
-{
- size_t result = 0;
-
- while (*str++ && result < maxToRead) {
- result += 1;
- }
-
- return result;
-}
-
-DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, const char* str, size_t maxToRead)
-{
- size_t len = drwav__strlen_clamped(str, maxToRead);
-
- if (len) {
- char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1);
- DRWAV_ASSERT(result != NULL);
-
- DRWAV_COPY_MEMORY(result, str, len);
- result[len] = '\0';
-
- return result;
- } else {
- return NULL;
- }
-}
-
-typedef struct
-{
- const void* pBuffer;
- size_t sizeInBytes;
- size_t cursor;
-} drwav_buffer_reader;
-
-DRWAV_PRIVATE drwav_result drwav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, drwav_buffer_reader* pReader)
-{
- DRWAV_ASSERT(pBuffer != NULL);
- DRWAV_ASSERT(pReader != NULL);
-
- DRWAV_ZERO_OBJECT(pReader);
-
- pReader->pBuffer = pBuffer;
- pReader->sizeInBytes = sizeInBytes;
- pReader->cursor = 0;
-
- return DRWAV_SUCCESS;
-}
-
-DRWAV_PRIVATE const void* drwav_buffer_reader_ptr(const drwav_buffer_reader* pReader)
-{
- DRWAV_ASSERT(pReader != NULL);
-
- return drwav_offset_ptr(pReader->pBuffer, pReader->cursor);
-}
-
-DRWAV_PRIVATE drwav_result drwav_buffer_reader_seek(drwav_buffer_reader* pReader, size_t bytesToSeek)
-{
- DRWAV_ASSERT(pReader != NULL);
-
- if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) {
- return DRWAV_BAD_SEEK; /* Seeking too far forward. */
- }
-
- pReader->cursor += bytesToSeek;
-
- return DRWAV_SUCCESS;
-}
-
-DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead)
-{
- drwav_result result = DRWAV_SUCCESS;
- size_t bytesRemaining;
-
- DRWAV_ASSERT(pReader != NULL);
-
- if (pBytesRead != NULL) {
- *pBytesRead = 0;
- }
-
- bytesRemaining = (pReader->sizeInBytes - pReader->cursor);
- if (bytesToRead > bytesRemaining) {
- bytesToRead = bytesRemaining;
- }
-
- if (pDst == NULL) {
- /* Seek. */
- result = drwav_buffer_reader_seek(pReader, bytesToRead);
- } else {
- /* Read. */
- DRWAV_COPY_MEMORY(pDst, drwav_buffer_reader_ptr(pReader), bytesToRead);
- pReader->cursor += bytesToRead;
- }
-
- DRWAV_ASSERT(pReader->cursor <= pReader->sizeInBytes);
-
- if (result == DRWAV_SUCCESS) {
- if (pBytesRead != NULL) {
- *pBytesRead = bytesToRead;
- }
- }
-
- return DRWAV_SUCCESS;
-}
-
-DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u16(drwav_buffer_reader* pReader, drwav_uint16* pDst)
-{
- drwav_result result;
- size_t bytesRead;
- drwav_uint8 data[2];
-
- DRWAV_ASSERT(pReader != NULL);
- DRWAV_ASSERT(pDst != NULL);
-
- *pDst = 0; /* Safety. */
-
- result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
- if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) {
- return result;
- }
-
- *pDst = drwav_bytes_to_u16(data);
-
- return DRWAV_SUCCESS;
-}
-
-DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u32(drwav_buffer_reader* pReader, drwav_uint32* pDst)
-{
- drwav_result result;
- size_t bytesRead;
- drwav_uint8 data[4];
-
- DRWAV_ASSERT(pReader != NULL);
- DRWAV_ASSERT(pDst != NULL);
-
- *pDst = 0; /* Safety. */
-
- result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
- if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) {
- return result;
- }
-
- *pDst = drwav_bytes_to_u32(data);
-
- return DRWAV_SUCCESS;
-}
-
-
-
-DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize)
-{
- drwav_uint8 bextData[DRWAV_BEXT_BYTES];
- size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL);
-
- DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
-
- if (bytesRead == sizeof(bextData)) {
- drwav_buffer_reader reader;
- drwav_uint32 timeReferenceLow;
- drwav_uint32 timeReferenceHigh;
- size_t extraBytes;
-
- pMetadata->type = drwav_metadata_type_bext;
-
- if (drwav_buffer_reader_init(bextData, bytesRead, &reader) == DRWAV_SUCCESS) {
- pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_DESCRIPTION_BYTES);
- drwav_buffer_reader_seek(&reader, DRWAV_BEXT_DESCRIPTION_BYTES);
-
- pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
- drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
-
- pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_REF_BYTES);
- drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_REF_BYTES);
-
- drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL);
- drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL);
-
- drwav_buffer_reader_read_u32(&reader, &timeReferenceLow);
- drwav_buffer_reader_read_u32(&reader, &timeReferenceHigh);
- pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow;
-
- drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version);
-
- pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1);
- drwav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES, NULL);
-
- drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue);
- drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange);
- drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel);
- drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness);
- drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness);
-
- DRWAV_ASSERT((drwav_offset_ptr(drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_RESERVED_BYTES)) == (bextData + DRWAV_BEXT_BYTES));
-
- extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES);
- if (extraBytes > 0) {
- pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1);
- DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL);
-
- bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL);
- pMetadata->data.bext.codingHistorySize = (drwav_uint32)drwav__strlen(pMetadata->data.bext.pCodingHistory);
- } else {
- pMetadata->data.bext.pCodingHistory = NULL;
- pMetadata->data.bext.codingHistorySize = 0;
- }
- }
- }
-
- return bytesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize, drwav_metadata_type type)
-{
- drwav_uint8 cueIDBuffer[DRWAV_LIST_LABEL_OR_NOTE_BYTES];
- drwav_uint64 totalBytesRead = 0;
- size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead);
-
- DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
-
- if (bytesJustRead == sizeof(cueIDBuffer)) {
- drwav_uint32 sizeIncludingNullTerminator;
-
- pMetadata->type = type;
- pMetadata->data.labelOrNote.cuePointId = drwav_bytes_to_u32(cueIDBuffer);
-
- sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES;
- if (sizeIncludingNullTerminator > 0) {
- pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1;
- pMetadata->data.labelOrNote.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
- DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
-
- drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead);
- } else {
- pMetadata->data.labelOrNote.stringLength = 0;
- pMetadata->data.labelOrNote.pString = NULL;
- }
- }
-
- return totalBytesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize)
-{
- drwav_uint8 buffer[DRWAV_LIST_LABELLED_TEXT_BYTES];
- drwav_uint64 totalBytesRead = 0;
- size_t bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead);
-
- DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read);
-
- if (bytesJustRead == sizeof(buffer)) {
- drwav_uint32 sizeIncludingNullTerminator;
-
- pMetadata->type = drwav_metadata_type_list_labelled_cue_region;
- pMetadata->data.labelledCueRegion.cuePointId = drwav_bytes_to_u32(buffer + 0);
- pMetadata->data.labelledCueRegion.sampleLength = drwav_bytes_to_u32(buffer + 4);
- pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8];
- pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9];
- pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10];
- pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11];
- pMetadata->data.labelledCueRegion.country = drwav_bytes_to_u16(buffer + 12);
- pMetadata->data.labelledCueRegion.language = drwav_bytes_to_u16(buffer + 14);
- pMetadata->data.labelledCueRegion.dialect = drwav_bytes_to_u16(buffer + 16);
- pMetadata->data.labelledCueRegion.codePage = drwav_bytes_to_u16(buffer + 18);
-
- sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABELLED_TEXT_BYTES;
- if (sizeIncludingNullTerminator > 0) {
- pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1;
- pMetadata->data.labelledCueRegion.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
- DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
-
- drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead);
- } else {
- pMetadata->data.labelledCueRegion.stringLength = 0;
- pMetadata->data.labelledCueRegion.pString = NULL;
- }
- }
-
- return totalBytesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metadata_parser* pParser, drwav_uint64 chunkSize, drwav_metadata_type type)
-{
- drwav_uint64 bytesRead = 0;
- drwav_uint32 stringSizeWithNullTerminator = (drwav_uint32)chunkSize;
-
- if (pParser->stage == drwav__metadata_parser_stage_count) {
- pParser->metadataCount += 1;
- drwav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1);
- } else {
- drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
- pMetadata->type = type;
- if (stringSizeWithNullTerminator > 0) {
- pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1;
- pMetadata->data.infoText.pString = (char*)drwav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1);
- DRWAV_ASSERT(pMetadata->data.infoText.pString != NULL);
-
- bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL);
- if (bytesRead == chunkSize) {
- pParser->metadataCursor += 1;
- } else {
- /* Failed to parse. */
- }
- } else {
- pMetadata->data.infoText.stringLength = 0;
- pMetadata->data.infoText.pString = NULL;
- pParser->metadataCursor += 1;
- }
- }
-
- return bytesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata_parser* pParser, const drwav_uint8* pChunkId, drwav_uint64 chunkSize, drwav_metadata_location location)
-{
- drwav_uint64 bytesRead = 0;
-
- if (location == drwav_metadata_location_invalid) {
- return 0;
- }
-
- if (drwav_fourcc_equal(pChunkId, "data") || drwav_fourcc_equal(pChunkId, "fmt ") || drwav_fourcc_equal(pChunkId, "fact")) {
- return 0;
- }
-
- if (pParser->stage == drwav__metadata_parser_stage_count) {
- pParser->metadataCount += 1;
- drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1);
- } else {
- drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
- pMetadata->type = drwav_metadata_type_unknown;
- pMetadata->data.unknown.chunkLocation = location;
- pMetadata->data.unknown.id[0] = pChunkId[0];
- pMetadata->data.unknown.id[1] = pChunkId[1];
- pMetadata->data.unknown.id[2] = pChunkId[2];
- pMetadata->data.unknown.id[3] = pChunkId[3];
- pMetadata->data.unknown.dataSizeInBytes = (drwav_uint32)chunkSize;
- pMetadata->data.unknown.pData = (drwav_uint8 *)drwav__metadata_get_memory(pParser, (size_t)chunkSize, 1);
- DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL);
-
- bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL);
- if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) {
- pParser->metadataCursor += 1;
- } else {
- /* Failed to read. */
- }
- }
-
- return bytesRead;
-}
-
-DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_metadata_type allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID)
-{
- return (allowedMetadataTypes & type) && drwav_fourcc_equal(pChunkID, pID);
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata_type allowedMetadataTypes)
-{
- const drwav_uint8 *pChunkID = pChunkHeader->id.fourcc;
- drwav_uint64 bytesRead = 0;
-
- if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_smpl, "smpl")) {
- if (pChunkHeader->sizeInBytes >= DRWAV_SMPL_BYTES) {
- if (pParser->stage == drwav__metadata_parser_stage_count) {
- drwav_uint8 buffer[4];
- size_t bytesJustRead;
-
- if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) {
- return bytesRead;
- }
- bytesRead += 28;
-
- bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
- if (bytesJustRead == sizeof(buffer)) {
- drwav_uint32 loopCount = drwav_bytes_to_u32(buffer);
- drwav_uint64 calculatedLoopCount;
-
- /* The loop count must be validated against the size of the chunk. */
- calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES;
- if (calculatedLoopCount == loopCount) {
- bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
- if (bytesJustRead == sizeof(buffer)) {
- drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer);
-
- pParser->metadataCount += 1;
- drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT);
- drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1);
- }
- } else {
- /* Loop count in header does not match the size of the chunk. */
- }
- }
- } else {
- bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
- if (bytesRead == pChunkHeader->sizeInBytes) {
- pParser->metadataCursor += 1;
- } else {
- /* Failed to parse. */
- }
- }
- } else {
- /* Incorrectly formed chunk. */
- }
- } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_inst, "inst")) {
- if (pChunkHeader->sizeInBytes == DRWAV_INST_BYTES) {
- if (pParser->stage == drwav__metadata_parser_stage_count) {
- pParser->metadataCount += 1;
- } else {
- bytesRead = drwav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
- if (bytesRead == pChunkHeader->sizeInBytes) {
- pParser->metadataCursor += 1;
- } else {
- /* Failed to parse. */
- }
- }
- } else {
- /* Incorrectly formed chunk. */
- }
- } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_acid, "acid")) {
- if (pChunkHeader->sizeInBytes == DRWAV_ACID_BYTES) {
- if (pParser->stage == drwav__metadata_parser_stage_count) {
- pParser->metadataCount += 1;
- } else {
- bytesRead = drwav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
- if (bytesRead == pChunkHeader->sizeInBytes) {
- pParser->metadataCursor += 1;
- } else {
- /* Failed to parse. */
- }
- }
- } else {
- /* Incorrectly formed chunk. */
- }
- } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_cue, "cue ")) {
- if (pChunkHeader->sizeInBytes >= DRWAV_CUE_BYTES) {
- if (pParser->stage == drwav__metadata_parser_stage_count) {
- size_t cueCount;
-
- pParser->metadataCount += 1;
- cueCount = (size_t)(pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES;
- drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_cue_point) * cueCount, DRWAV_METADATA_ALIGNMENT);
- } else {
- bytesRead = drwav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
- if (bytesRead == pChunkHeader->sizeInBytes) {
- pParser->metadataCursor += 1;
- } else {
- /* Failed to parse. */
- }
- }
- } else {
- /* Incorrectly formed chunk. */
- }
- } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_bext, "bext")) {
- if (pChunkHeader->sizeInBytes >= DRWAV_BEXT_BYTES) {
- if (pParser->stage == drwav__metadata_parser_stage_count) {
- /* The description field is the largest one in a bext chunk, so that is the max size of this temporary buffer. */
- char buffer[DRWAV_BEXT_DESCRIPTION_BYTES + 1];
- size_t allocSizeNeeded = DRWAV_BEXT_UMID_BYTES; /* We know we will need SMPTE umid size. */
- size_t bytesJustRead;
-
- buffer[DRWAV_BEXT_DESCRIPTION_BYTES] = '\0';
- bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_DESCRIPTION_BYTES, &bytesRead);
- if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) {
- return bytesRead;
- }
- allocSizeNeeded += drwav__strlen(buffer) + 1;
-
- buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0';
- bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead);
- if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) {
- return bytesRead;
- }
- allocSizeNeeded += drwav__strlen(buffer) + 1;
-
- buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0';
- bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead);
- if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) {
- return bytesRead;
- }
- allocSizeNeeded += drwav__strlen(buffer) + 1;
- allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; /* Coding history. */
-
- drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1);
-
- pParser->metadataCount += 1;
- } else {
- bytesRead = drwav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes);
- if (bytesRead == pChunkHeader->sizeInBytes) {
- pParser->metadataCursor += 1;
- } else {
- /* Failed to parse. */
- }
- }
- } else {
- /* Incorrectly formed chunk. */
- }
- } else if (drwav_fourcc_equal(pChunkID, "LIST") || drwav_fourcc_equal(pChunkID, "list")) {
- drwav_metadata_location listType = drwav_metadata_location_invalid;
- while (bytesRead < pChunkHeader->sizeInBytes) {
- drwav_uint8 subchunkId[4];
- drwav_uint8 subchunkSizeBuffer[4];
- drwav_uint64 subchunkDataSize;
- drwav_uint64 subchunkBytesRead = 0;
- drwav_uint64 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead);
- if (bytesJustRead != sizeof(subchunkId)) {
- break;
- }
-
- /*
- The first thing in a list chunk should be "adtl" or "INFO".
-
- - adtl means this list is a Associated Data List Chunk and will contain labels, notes
- or labelled cue regions.
- - INFO means this list is an Info List Chunk containing info text chunks such as IPRD
- which would specifies the album of this wav file.
-
- No data follows the adtl or INFO id so we just make note of what type this list is and
- continue.
- */
- if (drwav_fourcc_equal(subchunkId, "adtl")) {
- listType = drwav_metadata_location_inside_adtl_list;
- continue;
- } else if (drwav_fourcc_equal(subchunkId, "INFO")) {
- listType = drwav_metadata_location_inside_info_list;
- continue;
- }
-
- bytesJustRead = drwav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead);
- if (bytesJustRead != sizeof(subchunkSizeBuffer)) {
- break;
- }
- subchunkDataSize = drwav_bytes_to_u32(subchunkSizeBuffer);
-
- if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_label, "labl") || drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_note, "note")) {
- if (subchunkDataSize >= DRWAV_LIST_LABEL_OR_NOTE_BYTES) {
- drwav_uint64 stringSizeWithNullTerm = subchunkDataSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES;
- if (pParser->stage == drwav__metadata_parser_stage_count) {
- pParser->metadataCount += 1;
- drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1);
- } else {
- subchunkBytesRead = drwav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, drwav_fourcc_equal(subchunkId, "labl") ? drwav_metadata_type_list_label : drwav_metadata_type_list_note);
- if (subchunkBytesRead == subchunkDataSize) {
- pParser->metadataCursor += 1;
- } else {
- /* Failed to parse. */
- }
- }
- } else {
- /* Incorrectly formed chunk. */
- }
- } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_labelled_cue_region, "ltxt")) {
- if (subchunkDataSize >= DRWAV_LIST_LABELLED_TEXT_BYTES) {
- drwav_uint64 stringSizeWithNullTerminator = subchunkDataSize - DRWAV_LIST_LABELLED_TEXT_BYTES;
- if (pParser->stage == drwav__metadata_parser_stage_count) {
- pParser->metadataCount += 1;
- drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1);
- } else {
- subchunkBytesRead = drwav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize);
- if (subchunkBytesRead == subchunkDataSize) {
- pParser->metadataCursor += 1;
- } else {
- /* Failed to parse. */
- }
- }
- } else {
- /* Incorrectly formed chunk. */
- }
- } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_software, "ISFT")) {
- subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_software);
- } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_copyright, "ICOP")) {
- subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_copyright);
- } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_title, "INAM")) {
- subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_title);
- } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_artist, "IART")) {
- subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_artist);
- } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_comment, "ICMT")) {
- subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_comment);
- } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_date, "ICRD")) {
- subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_date);
- } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_genre, "IGNR")) {
- subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_genre);
- } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_album, "IPRD")) {
- subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album);
- } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) {
- subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber);
- } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) {
- subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType);
- }
-
- bytesRead += subchunkBytesRead;
- DRWAV_ASSERT(subchunkBytesRead <= subchunkDataSize);
-
- if (subchunkBytesRead < subchunkDataSize) {
- drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead;
-
- if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) {
- break;
- }
- bytesRead += bytesToSeek;
- }
-
- if ((subchunkDataSize % 2) == 1) {
- if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) {
- break;
- }
- bytesRead += 1;
- }
- }
- } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) {
- bytesRead = drwav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, drwav_metadata_location_top_level);
- }
-
- return bytesRead;
-}
-
-
-DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav)
-{
- drwav_uint32 bytesPerFrame;
-
- /*
- The bytes per frame is a bit ambiguous. It can be either be based on the bits per sample, or the block align. The way I'm doing it here
- is that if the bits per sample is a multiple of 8, use floor(bitsPerSample*channels/8), otherwise fall back to the block align.
- */
- if ((pWav->bitsPerSample & 0x7) == 0) {
- /* Bits per sample is a multiple of 8. */
- bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3;
- } else {
- bytesPerFrame = pWav->fmt.blockAlign;
- }
-
- /* Validation for known formats. a-law and mu-law should be 1 byte per channel. If it's not, it's not decodable. */
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
- if (bytesPerFrame != pWav->fmt.channels) {
- return 0; /* Invalid file. */
- }
- }
-
- return bytesPerFrame;
-}
-
-DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT)
-{
- if (pFMT == NULL) {
- return 0;
- }
-
- if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) {
- return pFMT->formatTag;
- } else {
- return drwav_bytes_to_u16(pFMT->subFormat); /* Only the first two bytes are required. */
- }
-}
-
-DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (pWav == NULL || onRead == NULL || onSeek == NULL) {
- return DRWAV_FALSE;
- }
-
- DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
- pWav->onRead = onRead;
- pWav->onSeek = onSeek;
- pWav->pUserData = pReadSeekUserData;
- pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
-
- if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
- return DRWAV_FALSE; /* Invalid allocation callbacks. */
- }
-
- return DRWAV_TRUE;
-}
-
-DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
-{
- /* This function assumes drwav_preinit() has been called beforehand. */
- drwav_result result;
- drwav_uint64 cursor; /* <-- Keeps track of the byte position so we can seek to specific locations. */
- drwav_bool32 sequential;
- drwav_uint8 riff[4];
- drwav_fmt fmt;
- unsigned short translatedFormatTag;
- drwav_uint64 dataChunkSize = 0; /* <-- Important! Don't explicitly set this to 0 anywhere else. Calculation of the size of the data chunk is performed in different paths depending on the container. */
- drwav_uint64 sampleCountFromFactChunk = 0; /* Same as dataChunkSize - make sure this is the only place this is initialized to 0. */
- drwav_uint64 metadataStartPos;
- drwav__metadata_parser metadataParser;
- drwav_bool8 isProcessingMetadata = DRWAV_FALSE;
- drwav_bool8 foundChunk_fmt = DRWAV_FALSE;
- drwav_bool8 foundChunk_data = DRWAV_FALSE;
- drwav_bool8 isAIFCFormType = DRWAV_FALSE; /* Only used with AIFF. */
- drwav_uint64 aiffFrameCount = 0;
-
- cursor = 0;
- sequential = (flags & DRWAV_SEQUENTIAL) != 0;
- DRWAV_ZERO_OBJECT(&fmt);
-
- /* The first 4 bytes should be the RIFF identifier. */
- if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {
- return DRWAV_FALSE;
- }
-
- /*
- The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for
- w64 it will start with "riff".
- */
- if (drwav_fourcc_equal(riff, "RIFF")) {
- pWav->container = drwav_container_riff;
- } else if (drwav_fourcc_equal(riff, "RIFX")) {
- pWav->container = drwav_container_rifx;
- } else if (drwav_fourcc_equal(riff, "riff")) {
- int i;
- drwav_uint8 riff2[12];
-
- pWav->container = drwav_container_w64;
-
- /* Check the rest of the GUID for validity. */
- if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {
- return DRWAV_FALSE;
- }
-
- for (i = 0; i < 12; ++i) {
- if (riff2[i] != drwavGUID_W64_RIFF[i+4]) {
- return DRWAV_FALSE;
- }
- }
- } else if (drwav_fourcc_equal(riff, "RF64")) {
- pWav->container = drwav_container_rf64;
- } else if (drwav_fourcc_equal(riff, "FORM")) {
- pWav->container = drwav_container_aiff;
- } else {
- return DRWAV_FALSE; /* Unknown or unsupported container. */
- }
-
-
- if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) {
- drwav_uint8 chunkSizeBytes[4];
- drwav_uint8 wave[4];
-
- if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
- return DRWAV_FALSE;
- }
-
- if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx) {
- if (drwav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) {
- return DRWAV_FALSE; /* Chunk size should always be at least 36 bytes. */
- }
- } else if (pWav->container == drwav_container_rf64) {
- if (drwav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) {
- return DRWAV_FALSE; /* Chunk size should always be set to -1/0xFFFFFFFF for RF64. The actual size is retrieved later. */
- }
- } else {
- return DRWAV_FALSE; /* Should never hit this. */
- }
-
- if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
- return DRWAV_FALSE;
- }
-
- if (!drwav_fourcc_equal(wave, "WAVE")) {
- return DRWAV_FALSE; /* Expecting "WAVE". */
- }
- } else if (pWav->container == drwav_container_w64) {
- drwav_uint8 chunkSizeBytes[8];
- drwav_uint8 wave[16];
-
- if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
- return DRWAV_FALSE;
- }
-
- if (drwav_bytes_to_u64(chunkSizeBytes) < 80) {
- return DRWAV_FALSE;
- }
-
- if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
- return DRWAV_FALSE;
- }
-
- if (!drwav_guid_equal(wave, drwavGUID_W64_WAVE)) {
- return DRWAV_FALSE;
- }
- } else if (pWav->container == drwav_container_aiff) {
- drwav_uint8 chunkSizeBytes[4];
- drwav_uint8 aiff[4];
-
- if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
- return DRWAV_FALSE;
- }
-
- if (drwav_bytes_to_u32_be(chunkSizeBytes) < 18) {
- return DRWAV_FALSE;
- }
-
- if (drwav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) {
- return DRWAV_FALSE;
- }
-
- if (drwav_fourcc_equal(aiff, "AIFF")) {
- isAIFCFormType = DRWAV_FALSE;
- } else if (drwav_fourcc_equal(aiff, "AIFC")) {
- isAIFCFormType = DRWAV_TRUE;
- } else {
- return DRWAV_FALSE; /* Expecting "AIFF" or "AIFC". */
- }
- } else {
- return DRWAV_FALSE;
- }
-
-
- /* For RF64, the "ds64" chunk must come next, before the "fmt " chunk. */
- if (pWav->container == drwav_container_rf64) {
- drwav_uint8 sizeBytes[8];
- drwav_uint64 bytesRemainingInChunk;
- drwav_chunk_header header;
- result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
- if (result != DRWAV_SUCCESS) {
- return DRWAV_FALSE;
- }
-
- if (!drwav_fourcc_equal(header.id.fourcc, "ds64")) {
- return DRWAV_FALSE; /* Expecting "ds64". */
- }
-
- bytesRemainingInChunk = header.sizeInBytes + header.paddingSize;
-
- /* We don't care about the size of the RIFF chunk - skip it. */
- if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) {
- return DRWAV_FALSE;
- }
- bytesRemainingInChunk -= 8;
- cursor += 8;
-
-
- /* Next 8 bytes is the size of the "data" chunk. */
- if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
- return DRWAV_FALSE;
- }
- bytesRemainingInChunk -= 8;
- dataChunkSize = drwav_bytes_to_u64(sizeBytes);
-
-
- /* Next 8 bytes is the same count which we would usually derived from the FACT chunk if it was available. */
- if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
- return DRWAV_FALSE;
- }
- bytesRemainingInChunk -= 8;
- sampleCountFromFactChunk = drwav_bytes_to_u64(sizeBytes);
-
-
- /* Skip over everything else. */
- if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) {
- return DRWAV_FALSE;
- }
- cursor += bytesRemainingInChunk;
- }
-
-
- metadataStartPos = cursor;
-
- /*
- Whether or not we are processing metadata controls how we load. We can load more efficiently when
- metadata is not being processed, but we also cannot process metadata for Wave64 because I have not
- been able to test it. If someone is able to test this and provide a patch I'm happy to enable it.
-
- Seqential mode cannot support metadata because it involves seeking backwards.
- */
- isProcessingMetadata = !sequential && ((flags & DRWAV_WITH_METADATA) != 0);
-
- /* Don't allow processing of metadata with untested containers. */
- if (pWav->container != drwav_container_riff && pWav->container != drwav_container_rf64) {
- isProcessingMetadata = DRWAV_FALSE;
- }
-
- DRWAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser));
- if (isProcessingMetadata) {
- metadataParser.onRead = pWav->onRead;
- metadataParser.onSeek = pWav->onSeek;
- metadataParser.pReadSeekUserData = pWav->pUserData;
- metadataParser.stage = drwav__metadata_parser_stage_count;
- }
-
-
- /*
- From here on out, chunks might be in any order. In order to robustly handle metadata we'll need
- to loop through every chunk and handle them as we find them. In sequential mode we need to get
- out of the loop as soon as we find the data chunk because we won't be able to seek back.
- */
- for (;;) { /* For each chunk... */
- drwav_chunk_header header;
- drwav_uint64 chunkSize;
-
- result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
- if (result != DRWAV_SUCCESS) {
- break;
- }
-
- chunkSize = header.sizeInBytes;
-
-
- /*
- Always tell the caller about this chunk. We cannot do this in sequential mode because the
- callback is allowed to read from the file, in which case we'll need to rewind.
- */
- if (!sequential && onChunk != NULL) {
- drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt);
-
- /*
- dr_wav may need to read the contents of the chunk, so we now need to seek back to the position before
- we called the callback.
- */
- if (callbackBytesRead > 0) {
- if (drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == DRWAV_FALSE) {
- return DRWAV_FALSE;
- }
- }
- }
-
-
- /* Explicitly handle known chunks first. */
-
- /* "fmt " */
- if (((pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) && drwav_fourcc_equal(header.id.fourcc, "fmt ")) ||
- ((pWav->container == drwav_container_w64) && drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT))) {
- drwav_uint8 fmtData[16];
-
- foundChunk_fmt = DRWAV_TRUE;
-
- if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) {
- return DRWAV_FALSE;
- }
- cursor += sizeof(fmtData);
-
- fmt.formatTag = drwav_bytes_to_u16_ex(fmtData + 0, pWav->container);
- fmt.channels = drwav_bytes_to_u16_ex(fmtData + 2, pWav->container);
- fmt.sampleRate = drwav_bytes_to_u32_ex(fmtData + 4, pWav->container);
- fmt.avgBytesPerSec = drwav_bytes_to_u32_ex(fmtData + 8, pWav->container);
- fmt.blockAlign = drwav_bytes_to_u16_ex(fmtData + 12, pWav->container);
- fmt.bitsPerSample = drwav_bytes_to_u16_ex(fmtData + 14, pWav->container);
-
- fmt.extendedSize = 0;
- fmt.validBitsPerSample = 0;
- fmt.channelMask = 0;
- DRWAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat));
-
- if (header.sizeInBytes > 16) {
- drwav_uint8 fmt_cbSize[2];
- int bytesReadSoFar = 0;
-
- if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
- return DRWAV_FALSE; /* Expecting more data. */
- }
- cursor += sizeof(fmt_cbSize);
-
- bytesReadSoFar = 18;
-
- fmt.extendedSize = drwav_bytes_to_u16_ex(fmt_cbSize, pWav->container);
- if (fmt.extendedSize > 0) {
- /* Simple validation. */
- if (fmt.formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
- if (fmt.extendedSize != 22) {
- return DRWAV_FALSE;
- }
- }
-
- if (fmt.formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
- drwav_uint8 fmtext[22];
-
- if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) {
- return DRWAV_FALSE; /* Expecting more data. */
- }
-
- fmt.validBitsPerSample = drwav_bytes_to_u16_ex(fmtext + 0, pWav->container);
- fmt.channelMask = drwav_bytes_to_u32_ex(fmtext + 2, pWav->container);
- drwav_bytes_to_guid(fmtext + 6, fmt.subFormat);
- } else {
- if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, drwav_seek_origin_current) == DRWAV_FALSE) {
- return DRWAV_FALSE;
- }
- }
- cursor += fmt.extendedSize;
-
- bytesReadSoFar += fmt.extendedSize;
- }
-
- /* Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. */
- if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current) == DRWAV_FALSE) {
- return DRWAV_FALSE;
- }
- cursor += (header.sizeInBytes - bytesReadSoFar);
- }
-
- if (header.paddingSize > 0) {
- if (drwav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == DRWAV_FALSE) {
- break;
- }
- cursor += header.paddingSize;
- }
-
- /* Go to the next chunk. Don't include this chunk in metadata. */
- continue;
- }
-
- /* "data" */
- if (((pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) && drwav_fourcc_equal(header.id.fourcc, "data")) ||
- ((pWav->container == drwav_container_w64) && drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA))) {
- foundChunk_data = DRWAV_TRUE;
-
- pWav->dataChunkDataPos = cursor;
-
- if (pWav->container != drwav_container_rf64) { /* The data chunk size for RF64 will always be set to 0xFFFFFFFF here. It was set to it's true value earlier. */
- dataChunkSize = chunkSize;
- }
-
- /* If we're running in sequential mode, or we're not reading metadata, we have enough now that we can get out of the loop. */
- if (sequential || !isProcessingMetadata) {
- break; /* No need to keep reading beyond the data chunk. */
- } else {
- chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */
- if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) {
- break;
- }
- cursor += chunkSize;
-
- continue; /* There may be some more metadata to read. */
- }
- }
-
- /* "fact". This is optional. Can use this to get the sample count which is useful for compressed formats. For RF64 we retrieved the sample count from the ds64 chunk earlier. */
- if (((pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx || pWav->container == drwav_container_rf64) && drwav_fourcc_equal(header.id.fourcc, "fact")) ||
- ((pWav->container == drwav_container_w64) && drwav_guid_equal(header.id.guid, drwavGUID_W64_FACT))) {
- if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx) {
- drwav_uint8 sampleCount[4];
- if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) {
- return DRWAV_FALSE;
- }
-
- chunkSize -= 4;
-
- /*
- The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this
- for Microsoft ADPCM formats.
- */
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
- sampleCountFromFactChunk = drwav_bytes_to_u32_ex(sampleCount, pWav->container);
- } else {
- sampleCountFromFactChunk = 0;
- }
- } else if (pWav->container == drwav_container_w64) {
- if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {
- return DRWAV_FALSE;
- }
-
- chunkSize -= 8;
- } else if (pWav->container == drwav_container_rf64) {
- /* We retrieved the sample count from the ds64 chunk earlier so no need to do that here. */
- }
-
- /* Seek to the next chunk in preparation for the next iteration. */
- chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */
- if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) {
- break;
- }
- cursor += chunkSize;
-
- continue;
- }
-
-
- /* "COMM". AIFF/AIFC only. */
- if (pWav->container == drwav_container_aiff && drwav_fourcc_equal(header.id.fourcc, "COMM")) {
- drwav_uint8 commData[24];
- drwav_uint32 commDataBytesToRead;
- drwav_uint16 channels;
- drwav_uint32 frameCount;
- drwav_uint16 sampleSizeInBits;
- drwav_int64 sampleRate;
- drwav_uint16 compressionFormat;
-
- foundChunk_fmt = DRWAV_TRUE;
-
- if (isAIFCFormType) {
- commDataBytesToRead = 24;
- if (header.sizeInBytes < commDataBytesToRead) {
- return DRWAV_FALSE; /* Invalid COMM chunk. */
- }
- } else {
- commDataBytesToRead = 18;
- if (header.sizeInBytes != commDataBytesToRead) {
- return DRWAV_FALSE; /* INVALID COMM chunk. */
- }
- }
-
- if (drwav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) {
- return DRWAV_FALSE;
- }
-
-
- channels = drwav_bytes_to_u16_ex (commData + 0, pWav->container);
- frameCount = drwav_bytes_to_u32_ex (commData + 2, pWav->container);
- sampleSizeInBits = drwav_bytes_to_u16_ex (commData + 6, pWav->container);
- sampleRate = drwav_aiff_extented_to_s64(commData + 8);
-
- if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) {
- return DRWAV_FALSE; /* Invalid sample rate. */
- }
-
- if (isAIFCFormType) {
- const drwav_uint8* type = commData + 18;
-
- if (drwav_fourcc_equal(type, "NONE")) {
- compressionFormat = DR_WAVE_FORMAT_PCM; /* PCM, big-endian. */
- } else if (drwav_fourcc_equal(type, "raw ")) {
- compressionFormat = DR_WAVE_FORMAT_PCM;
-
- /* In my testing, it looks like when the "raw " compression type is used, 8-bit samples should be considered unsigned. */
- if (sampleSizeInBits == 8) {
- pWav->aiff.isUnsigned = DRWAV_TRUE;
- }
- } else if (drwav_fourcc_equal(type, "sowt")) {
- compressionFormat = DR_WAVE_FORMAT_PCM; /* PCM, little-endian. */
- pWav->aiff.isLE = DRWAV_TRUE;
- } else if (drwav_fourcc_equal(type, "fl32") || drwav_fourcc_equal(type, "fl64") || drwav_fourcc_equal(type, "FL32") || drwav_fourcc_equal(type, "FL64")) {
- compressionFormat = DR_WAVE_FORMAT_IEEE_FLOAT;
- } else if (drwav_fourcc_equal(type, "alaw") || drwav_fourcc_equal(type, "ALAW")) {
- compressionFormat = DR_WAVE_FORMAT_ALAW;
- } else if (drwav_fourcc_equal(type, "ulaw") || drwav_fourcc_equal(type, "ULAW")) {
- compressionFormat = DR_WAVE_FORMAT_MULAW;
- } else if (drwav_fourcc_equal(type, "ima4")) {
- compressionFormat = DR_WAVE_FORMAT_DVI_ADPCM;
- sampleSizeInBits = 4;
-
- /*
- I haven't been able to figure out how to get correct decoding for IMA ADPCM. Until this is figured out
- we'll need to abort when we encounter such an encoding. Advice welcome!
- */
- return DRWAV_FALSE;
- } else {
- return DRWAV_FALSE; /* Unknown or unsupported compression format. Need to abort. */
- }
- } else {
- compressionFormat = DR_WAVE_FORMAT_PCM; /* It's a standard AIFF form which is always compressed. */
- }
-
- /* With AIFF we want to use the explicitly defined frame count rather than deriving it from the size of the chunk. */
- aiffFrameCount = frameCount;
-
- /* We should now have enough information to fill out our fmt structure. */
- fmt.formatTag = compressionFormat;
- fmt.channels = channels;
- fmt.sampleRate = (drwav_uint32)sampleRate;
- fmt.bitsPerSample = sampleSizeInBits;
- fmt.blockAlign = (drwav_uint16)(fmt.channels * fmt.bitsPerSample / 8);
- fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate;
-
- if (fmt.blockAlign == 0 && compressionFormat == DR_WAVE_FORMAT_DVI_ADPCM) {
- fmt.blockAlign = 34 * fmt.channels;
- }
-
- /*
- Weird one. I've seen some alaw and ulaw encoded files that for some reason set the bits per sample to 16 when
- it should be 8. To get this working I need to explicitly check for this and change it.
- */
- if (compressionFormat == DR_WAVE_FORMAT_ALAW || compressionFormat == DR_WAVE_FORMAT_MULAW) {
- if (fmt.bitsPerSample > 8) {
- fmt.bitsPerSample = 8;
- fmt.blockAlign = fmt.channels;
- }
- }
-
- /* In AIFF, samples are padded to 8 byte boundaries. We need to round up our bits per sample here. */
- fmt.bitsPerSample += (fmt.bitsPerSample & 7);
-
-
- /* If the form type is AIFC there will be some additional data in the chunk. We need to seek past it. */
- if (isAIFCFormType) {
- if (drwav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == DRWAV_FALSE) {
- return DRWAV_FALSE;
- }
- cursor += (chunkSize - commDataBytesToRead);
- }
-
- /* Don't fall through or else we'll end up treating this chunk as metadata which is incorrect. */
- continue;
- }
-
-
- /* "SSND". AIFF/AIFC only. This is the AIFF equivalent of the "data" chunk. */
- if (pWav->container == drwav_container_aiff && drwav_fourcc_equal(header.id.fourcc, "SSND")) {
- drwav_uint8 offsetAndBlockSizeData[8];
- drwav_uint32 offset;
-
- foundChunk_data = DRWAV_TRUE;
-
- if (drwav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) {
- return DRWAV_FALSE;
- }
-
- /* We need to seek forward by the offset. */
- offset = drwav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container);
- if (drwav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == DRWAV_FALSE) {
- return DRWAV_FALSE;
- }
- cursor += offset;
-
- pWav->dataChunkDataPos = cursor;
- dataChunkSize = chunkSize;
-
- /* If we're running in sequential mode, or we're not reading metadata, we have enough now that we can get out of the loop. */
- if (sequential || !isProcessingMetadata) {
- break; /* No need to keep reading beyond the data chunk. */
- } else {
- if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) {
- break;
- }
- cursor += chunkSize;
-
- continue; /* There may be some more metadata to read. */
- }
- }
-
-
-
- /* Getting here means it's not a chunk that we care about internally, but might need to be handled as metadata by the caller. */
- if (isProcessingMetadata) {
- drwav_uint64 metadataBytesRead;
-
- metadataBytesRead = drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown);
- DRWAV_ASSERT(metadataBytesRead <= header.sizeInBytes);
-
- /* Go back to the start of the chunk so we can normalize the position of the cursor. */
- if (drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == DRWAV_FALSE) {
- break; /* Failed to seek. Can't reliable read the remaining chunks. Get out. */
- }
- }
-
-
- /* Make sure we skip past the content of this chunk before we go to the next one. */
- chunkSize += header.paddingSize; /* <-- Make sure we seek past the padding. */
- if (drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == DRWAV_FALSE) {
- break;
- }
- cursor += chunkSize;
- }
-
- /* There's some mandatory chunks that must exist. If they were not found in the iteration above we must abort. */
- if (!foundChunk_fmt || !foundChunk_data) {
- return DRWAV_FALSE;
- }
-
- /* Basic validation. */
- if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE ) ||
- (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS ) ||
- (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) ||
- fmt.blockAlign == 0) {
- return DRWAV_FALSE; /* Probably an invalid WAV file. */
- }
-
- /* Translate the internal format. */
- translatedFormatTag = fmt.formatTag;
- if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
- translatedFormatTag = drwav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container);
- }
-
- /* We may have moved passed the data chunk. If so we need to move back. If running in sequential mode we can assume we are already sitting on the data chunk. */
- if (!sequential) {
- if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) {
- return DRWAV_FALSE;
- }
- cursor = pWav->dataChunkDataPos;
- }
-
-
- /*
- At this point we should have done the initial parsing of each of our chunks, but we now need to
- do a second pass to extract the actual contents of the metadata (the first pass just calculated
- the length of the memory allocation).
-
- We only do this if we've actually got metadata to parse.
- */
- if (isProcessingMetadata && metadataParser.metadataCount > 0) {
- if (drwav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == DRWAV_FALSE) {
- return DRWAV_FALSE;
- }
-
- result = drwav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks);
- if (result != DRWAV_SUCCESS) {
- return DRWAV_FALSE;
- }
-
- metadataParser.stage = drwav__metadata_parser_stage_read;
-
- for (;;) {
- drwav_chunk_header header;
- drwav_uint64 metadataBytesRead;
-
- result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
- if (result != DRWAV_SUCCESS) {
- break;
- }
-
- metadataBytesRead = drwav__metadata_process_chunk(&metadataParser, &header, drwav_metadata_type_all_including_unknown);
-
- /* Move to the end of the chunk so we can keep iterating. */
- if (drwav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == DRWAV_FALSE) {
- drwav_free(metadataParser.pMetadata, &pWav->allocationCallbacks);
- return DRWAV_FALSE;
- }
- }
-
- /* Getting here means we're finished parsing the metadata. */
- pWav->pMetadata = metadataParser.pMetadata;
- pWav->metadataCount = metadataParser.metadataCount;
- }
-
-
- /* At this point we should be sitting on the first byte of the raw audio data. */
-
- /*
- I've seen a WAV file in the wild where a RIFF-ecapsulated file has the size of it's "RIFF" and
- "data" chunks set to 0xFFFFFFFF when the file is definitely not that big. In this case we're
- going to have to calculate the size by reading and discarding bytes, and then seeking back. We
- cannot do this in sequential mode. We just assume that the rest of the file is audio data.
- */
- if (dataChunkSize == 0xFFFFFFFF && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rifx) && pWav->isSequentialWrite == DRWAV_FALSE) {
- dataChunkSize = 0;
-
- for (;;) {
- drwav_uint8 temp[4096];
- size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp));
- dataChunkSize += bytesRead;
-
- if (bytesRead < sizeof(temp)) {
- break;
- }
- }
- }
-
- if (drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == DRWAV_FALSE) {
- drwav_free(pWav->pMetadata, &pWav->allocationCallbacks);
- return DRWAV_FALSE;
- }
-
-
- pWav->fmt = fmt;
- pWav->sampleRate = fmt.sampleRate;
- pWav->channels = fmt.channels;
- pWav->bitsPerSample = fmt.bitsPerSample;
- pWav->bytesRemaining = dataChunkSize;
- pWav->translatedFormatTag = translatedFormatTag;
- pWav->dataChunkDataSize = dataChunkSize;
-
- if (sampleCountFromFactChunk != 0) {
- pWav->totalPCMFrameCount = sampleCountFromFactChunk;
- } else if (aiffFrameCount != 0) {
- pWav->totalPCMFrameCount = aiffFrameCount;
- } else {
- drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- drwav_free(pWav->pMetadata, &pWav->allocationCallbacks);
- return DRWAV_FALSE; /* Invalid file. */
- }
-
- pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame;
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
- drwav_uint64 totalBlockHeaderSizeInBytes;
- drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
-
- /* Make sure any trailing partial block is accounted for. */
- if ((blockCount * fmt.blockAlign) < dataChunkSize) {
- blockCount += 1;
- }
-
- /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */
- totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels);
- pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
- }
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
- drwav_uint64 totalBlockHeaderSizeInBytes;
- drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
-
- /* Make sure any trailing partial block is accounted for. */
- if ((blockCount * fmt.blockAlign) < dataChunkSize) {
- blockCount += 1;
- }
-
- /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */
- totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels);
- pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
-
- /* The header includes a decoded sample for each channel which acts as the initial predictor sample. */
- pWav->totalPCMFrameCount += blockCount;
- }
- }
-
- /* Some formats only support a certain number of channels. */
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
- if (pWav->channels > 2) {
- drwav_free(pWav->pMetadata, &pWav->allocationCallbacks);
- return DRWAV_FALSE;
- }
- }
-
- /* The number of bytes per frame must be known. If not, it's an invalid file and not decodable. */
- if (drwav_get_bytes_per_pcm_frame(pWav) == 0) {
- drwav_free(pWav->pMetadata, &pWav->allocationCallbacks);
- return DRWAV_FALSE;
- }
-
-#ifdef DR_WAV_LIBSNDFILE_COMPAT
- /*
- I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website),
- it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count
- from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct
- way to know the total sample count is to inspect the "fact" chunk, which should always be present for compressed formats, and should
- always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my
- correctness tests against libsndfile, and is disabled by default.
- */
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
- drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
- pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; /* x2 because two samples per byte. */
- }
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
- drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
- pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;
- }
-#endif
-
- return DRWAV_TRUE;
-}
-
-DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks);
-}
-
-DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) {
- return DRWAV_FALSE;
- }
-
- return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
-}
-
-DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
- return DRWAV_FALSE;
- }
-
- return drwav_init__internal(pWav, NULL, NULL, flags | DRWAV_WITH_METADATA);
-}
-
-DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav)
-{
- drwav_metadata *result = pWav->pMetadata;
-
- pWav->pMetadata = NULL;
- pWav->metadataCount = 0;
-
- return result;
-}
-
-
-DRWAV_PRIVATE size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize)
-{
- DRWAV_ASSERT(pWav != NULL);
- DRWAV_ASSERT(pWav->onWrite != NULL);
-
- /* Generic write. Assumes no byte reordering required. */
- return pWav->onWrite(pWav->pUserData, pData, dataSize);
-}
-
-DRWAV_PRIVATE size_t drwav__write_byte(drwav* pWav, drwav_uint8 byte)
-{
- DRWAV_ASSERT(pWav != NULL);
- DRWAV_ASSERT(pWav->onWrite != NULL);
-
- return pWav->onWrite(pWav->pUserData, &byte, 1);
-}
-
-DRWAV_PRIVATE size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value)
-{
- DRWAV_ASSERT(pWav != NULL);
- DRWAV_ASSERT(pWav->onWrite != NULL);
-
- if (!drwav__is_little_endian()) {
- value = drwav__bswap16(value);
- }
-
- return drwav__write(pWav, &value, 2);
-}
-
-DRWAV_PRIVATE size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value)
-{
- DRWAV_ASSERT(pWav != NULL);
- DRWAV_ASSERT(pWav->onWrite != NULL);
-
- if (!drwav__is_little_endian()) {
- value = drwav__bswap32(value);
- }
-
- return drwav__write(pWav, &value, 4);
-}
-
-DRWAV_PRIVATE size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value)
-{
- DRWAV_ASSERT(pWav != NULL);
- DRWAV_ASSERT(pWav->onWrite != NULL);
-
- if (!drwav__is_little_endian()) {
- value = drwav__bswap64(value);
- }
-
- return drwav__write(pWav, &value, 8);
-}
-
-DRWAV_PRIVATE size_t drwav__write_f32ne_to_le(drwav* pWav, float value)
-{
- union {
- drwav_uint32 u32;
- float f32;
- } u;
-
- DRWAV_ASSERT(pWav != NULL);
- DRWAV_ASSERT(pWav->onWrite != NULL);
-
- u.f32 = value;
-
- if (!drwav__is_little_endian()) {
- u.u32 = drwav__bswap32(u.u32);
- }
-
- return drwav__write(pWav, &u.u32, 4);
-}
-
-DRWAV_PRIVATE size_t drwav__write_or_count(drwav* pWav, const void* pData, size_t dataSize)
-{
- if (pWav == NULL) {
- return dataSize;
- }
-
- return drwav__write(pWav, pData, dataSize);
-}
-
-DRWAV_PRIVATE size_t drwav__write_or_count_byte(drwav* pWav, drwav_uint8 byte)
-{
- if (pWav == NULL) {
- return 1;
- }
-
- return drwav__write_byte(pWav, byte);
-}
-
-DRWAV_PRIVATE size_t drwav__write_or_count_u16ne_to_le(drwav* pWav, drwav_uint16 value)
-{
- if (pWav == NULL) {
- return 2;
- }
-
- return drwav__write_u16ne_to_le(pWav, value);
-}
-
-DRWAV_PRIVATE size_t drwav__write_or_count_u32ne_to_le(drwav* pWav, drwav_uint32 value)
-{
- if (pWav == NULL) {
- return 4;
- }
-
- return drwav__write_u32ne_to_le(pWav, value);
-}
-
-#if 0 /* Unused for now. */
-DRWAV_PRIVATE size_t drwav__write_or_count_u64ne_to_le(drwav* pWav, drwav_uint64 value)
-{
- if (pWav == NULL) {
- return 8;
- }
-
- return drwav__write_u64ne_to_le(pWav, value);
-}
-#endif
-
-DRWAV_PRIVATE size_t drwav__write_or_count_f32ne_to_le(drwav* pWav, float value)
-{
- if (pWav == NULL) {
- return 4;
- }
-
- return drwav__write_f32ne_to_le(pWav, value);
-}
-
-DRWAV_PRIVATE size_t drwav__write_or_count_string_to_fixed_size_buf(drwav* pWav, char* str, size_t bufFixedSize)
-{
- size_t len;
-
- if (pWav == NULL) {
- return bufFixedSize;
- }
-
- len = drwav__strlen_clamped(str, bufFixedSize);
- drwav__write_or_count(pWav, str, len);
-
- if (len < bufFixedSize) {
- size_t i;
- for (i = 0; i < bufFixedSize - len; ++i) {
- drwav__write_byte(pWav, 0);
- }
- }
-
- return bufFixedSize;
-}
-
-
-/* pWav can be NULL meaning just count the bytes that would be written. */
-DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* pMetadatas, drwav_uint32 metadataCount)
-{
- size_t bytesWritten = 0;
- drwav_bool32 hasListAdtl = DRWAV_FALSE;
- drwav_bool32 hasListInfo = DRWAV_FALSE;
- drwav_uint32 iMetadata;
-
- if (pMetadatas == NULL || metadataCount == 0) {
- return 0;
- }
-
- for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
- drwav_metadata* pMetadata = &pMetadatas[iMetadata];
- drwav_uint32 chunkSize = 0;
-
- if ((pMetadata->type & drwav_metadata_type_list_all_info_strings) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list)) {
- hasListInfo = DRWAV_TRUE;
- }
-
- if ((pMetadata->type & drwav_metadata_type_list_all_adtl) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list)) {
- hasListAdtl = DRWAV_TRUE;
- }
-
- switch (pMetadata->type) {
- case drwav_metadata_type_smpl:
- {
- drwav_uint32 iLoop;
-
- chunkSize = DRWAV_SMPL_BYTES + DRWAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes;
-
- bytesWritten += drwav__write_or_count(pWav, "smpl", 4);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
-
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
-
- for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) {
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount);
- }
-
- if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
- }
- } break;
-
- case drwav_metadata_type_inst:
- {
- chunkSize = DRWAV_INST_BYTES;
-
- bytesWritten += drwav__write_or_count(pWav, "inst", 4);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
- bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1);
- bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1);
- bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1);
- bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1);
- bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1);
- bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1);
- bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1);
- } break;
-
- case drwav_metadata_type_cue:
- {
- drwav_uint32 iCuePoint;
-
- chunkSize = DRWAV_CUE_BYTES + DRWAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount;
-
- bytesWritten += drwav__write_or_count(pWav, "cue ", 4);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount);
- for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition);
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset);
- }
- } break;
-
- case drwav_metadata_type_acid:
- {
- chunkSize = DRWAV_ACID_BYTES;
-
- bytesWritten += drwav__write_or_count(pWav, "acid", 4);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags);
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote);
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1);
- bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats);
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator);
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator);
- bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo);
- } break;
-
- case drwav_metadata_type_bext:
- {
- char reservedBuf[DRWAV_BEXT_RESERVED_BYTES];
- drwav_uint32 timeReferenceLow;
- drwav_uint32 timeReferenceHigh;
-
- chunkSize = DRWAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize;
-
- bytesWritten += drwav__write_or_count(pWav, "bext", 4);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
-
- bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, DRWAV_BEXT_DESCRIPTION_BYTES);
- bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, DRWAV_BEXT_ORIGINATOR_NAME_BYTES);
- bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, DRWAV_BEXT_ORIGINATOR_REF_BYTES);
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate));
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime));
-
- timeReferenceLow = (drwav_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF);
- timeReferenceHigh = (drwav_uint32)(pMetadata->data.bext.timeReference >> 32);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceLow);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh);
-
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version);
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES);
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue);
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange);
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel);
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness);
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness);
-
- DRWAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf));
- bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf));
-
- if (pMetadata->data.bext.codingHistorySize > 0) {
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize);
- }
- } break;
-
- case drwav_metadata_type_unknown:
- {
- if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_top_level) {
- chunkSize = pMetadata->data.unknown.dataSizeInBytes;
-
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes);
- }
- } break;
-
- default: break;
- }
- if ((chunkSize % 2) != 0) {
- bytesWritten += drwav__write_or_count_byte(pWav, 0);
- }
- }
-
- if (hasListInfo) {
- drwav_uint32 chunkSize = 4; /* Start with 4 bytes for "INFO". */
- for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
- drwav_metadata* pMetadata = &pMetadatas[iMetadata];
-
- if ((pMetadata->type & drwav_metadata_type_list_all_info_strings)) {
- chunkSize += 8; /* For id and string size. */
- chunkSize += pMetadata->data.infoText.stringLength + 1; /* Include null terminator. */
- } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) {
- chunkSize += 8; /* For id string size. */
- chunkSize += pMetadata->data.unknown.dataSizeInBytes;
- }
-
- if ((chunkSize % 2) != 0) {
- chunkSize += 1;
- }
- }
-
- bytesWritten += drwav__write_or_count(pWav, "LIST", 4);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
- bytesWritten += drwav__write_or_count(pWav, "INFO", 4);
-
- for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
- drwav_metadata* pMetadata = &pMetadatas[iMetadata];
- drwav_uint32 subchunkSize = 0;
-
- if (pMetadata->type & drwav_metadata_type_list_all_info_strings) {
- const char* pID = NULL;
-
- switch (pMetadata->type) {
- case drwav_metadata_type_list_info_software: pID = "ISFT"; break;
- case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break;
- case drwav_metadata_type_list_info_title: pID = "INAM"; break;
- case drwav_metadata_type_list_info_artist: pID = "IART"; break;
- case drwav_metadata_type_list_info_comment: pID = "ICMT"; break;
- case drwav_metadata_type_list_info_date: pID = "ICRD"; break;
- case drwav_metadata_type_list_info_genre: pID = "IGNR"; break;
- case drwav_metadata_type_list_info_album: pID = "IPRD"; break;
- case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break;
- default: break;
- }
-
- DRWAV_ASSERT(pID != NULL);
-
- if (pMetadata->data.infoText.stringLength) {
- subchunkSize = pMetadata->data.infoText.stringLength + 1;
- bytesWritten += drwav__write_or_count(pWav, pID, 4);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength);
- bytesWritten += drwav__write_or_count_byte(pWav, '\0');
- }
- } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) {
- if (pMetadata->data.unknown.dataSizeInBytes) {
- subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
-
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes);
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
- }
- }
-
- if ((subchunkSize % 2) != 0) {
- bytesWritten += drwav__write_or_count_byte(pWav, 0);
- }
- }
- }
-
- if (hasListAdtl) {
- drwav_uint32 chunkSize = 4; /* start with 4 bytes for "adtl" */
-
- for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
- drwav_metadata* pMetadata = &pMetadatas[iMetadata];
-
- switch (pMetadata->type)
- {
- case drwav_metadata_type_list_label:
- case drwav_metadata_type_list_note:
- {
- chunkSize += 8; /* for id and chunk size */
- chunkSize += DRWAV_LIST_LABEL_OR_NOTE_BYTES;
-
- if (pMetadata->data.labelOrNote.stringLength > 0) {
- chunkSize += pMetadata->data.labelOrNote.stringLength + 1;
- }
- } break;
-
- case drwav_metadata_type_list_labelled_cue_region:
- {
- chunkSize += 8; /* for id and chunk size */
- chunkSize += DRWAV_LIST_LABELLED_TEXT_BYTES;
-
- if (pMetadata->data.labelledCueRegion.stringLength > 0) {
- chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
- }
- } break;
-
- case drwav_metadata_type_unknown:
- {
- if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) {
- chunkSize += 8; /* for id and chunk size */
- chunkSize += pMetadata->data.unknown.dataSizeInBytes;
- }
- } break;
-
- default: break;
- }
-
- if ((chunkSize % 2) != 0) {
- chunkSize += 1;
- }
- }
-
- bytesWritten += drwav__write_or_count(pWav, "LIST", 4);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize);
- bytesWritten += drwav__write_or_count(pWav, "adtl", 4);
-
- for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
- drwav_metadata* pMetadata = &pMetadatas[iMetadata];
- drwav_uint32 subchunkSize = 0;
-
- switch (pMetadata->type)
- {
- case drwav_metadata_type_list_label:
- case drwav_metadata_type_list_note:
- {
- if (pMetadata->data.labelOrNote.stringLength > 0) {
- const char *pID = NULL;
-
- if (pMetadata->type == drwav_metadata_type_list_label) {
- pID = "labl";
- }
- else if (pMetadata->type == drwav_metadata_type_list_note) {
- pID = "note";
- }
-
- DRWAV_ASSERT(pID != NULL);
- DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
-
- subchunkSize = DRWAV_LIST_LABEL_OR_NOTE_BYTES;
-
- bytesWritten += drwav__write_or_count(pWav, pID, 4);
- subchunkSize += pMetadata->data.labelOrNote.stringLength + 1;
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
-
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId);
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength);
- bytesWritten += drwav__write_or_count_byte(pWav, '\0');
- }
- } break;
-
- case drwav_metadata_type_list_labelled_cue_region:
- {
- subchunkSize = DRWAV_LIST_LABELLED_TEXT_BYTES;
-
- bytesWritten += drwav__write_or_count(pWav, "ltxt", 4);
- if (pMetadata->data.labelledCueRegion.stringLength > 0) {
- subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
- }
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength);
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4);
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country);
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language);
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect);
- bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage);
-
- if (pMetadata->data.labelledCueRegion.stringLength > 0) {
- DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
-
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength);
- bytesWritten += drwav__write_or_count_byte(pWav, '\0');
- }
- } break;
-
- case drwav_metadata_type_unknown:
- {
- if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) {
- subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
-
- DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL);
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
- bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize);
- bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
- }
- } break;
-
- default: break;
- }
-
- if ((subchunkSize % 2) != 0) {
- bytesWritten += drwav__write_or_count_byte(pWav, 0);
- }
- }
- }
-
- DRWAV_ASSERT((bytesWritten % 2) == 0);
-
- return bytesWritten;
-}
-
-DRWAV_PRIVATE drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
-{
- drwav_uint64 chunkSize = 4 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); /* 4 = "WAVE". 24 = "fmt " chunk. 8 = "data" + u32 data size. */
- if (chunkSize > 0xFFFFFFFFUL) {
- chunkSize = 0xFFFFFFFFUL;
- }
-
- return (drwav_uint32)chunkSize; /* Safe cast due to the clamp above. */
-}
-
-DRWAV_PRIVATE drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize)
-{
- if (dataChunkSize <= 0xFFFFFFFFUL) {
- return (drwav_uint32)dataChunkSize;
- } else {
- return 0xFFFFFFFFUL;
- }
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize)
-{
- drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize);
-
- return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; /* +24 because W64 includes the size of the GUID and size fields. */
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize)
-{
- return 24 + dataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize, drwav_metadata *metadata, drwav_uint32 numMetadata)
-{
- drwav_uint64 chunkSize = 4 + 36 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); /* 4 = "WAVE". 36 = "ds64" chunk. 24 = "fmt " chunk. 8 = "data" + u32 data size. */
- if (chunkSize > 0xFFFFFFFFUL) {
- chunkSize = 0xFFFFFFFFUL;
- }
-
- return chunkSize;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize)
-{
- return dataChunkSize;
-}
-
-
-
-DRWAV_PRIVATE drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (pWav == NULL || onWrite == NULL) {
- return DRWAV_FALSE;
- }
-
- if (!isSequential && onSeek == NULL) {
- return DRWAV_FALSE; /* <-- onSeek is required when in non-sequential mode. */
- }
-
- /* Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this. */
- if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) {
- return DRWAV_FALSE;
- }
- if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) {
- return DRWAV_FALSE;
- }
-
- DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
- pWav->onWrite = onWrite;
- pWav->onSeek = onSeek;
- pWav->pUserData = pUserData;
- pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
-
- if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
- return DRWAV_FALSE; /* Invalid allocation callbacks. */
- }
-
- pWav->fmt.formatTag = (drwav_uint16)pFormat->format;
- pWav->fmt.channels = (drwav_uint16)pFormat->channels;
- pWav->fmt.sampleRate = pFormat->sampleRate;
- pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);
- pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);
- pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
- pWav->fmt.extendedSize = 0;
- pWav->isSequentialWrite = isSequential;
-
- return DRWAV_TRUE;
-}
-
-
-DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
-{
- /* The function assumes drwav_preinit_write() was called beforehand. */
-
- size_t runningPos = 0;
- drwav_uint64 initialDataChunkSize = 0;
- drwav_uint64 chunkSizeFMT;
-
- /*
- The initial values for the "RIFF" and "data" chunks depends on whether or not we are initializing in sequential mode or not. In
- sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non-
- sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek.
- */
- if (pWav->isSequentialWrite) {
- initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;
-
- /*
- The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64
- so for the sake of simplicity I'm not doing any validation for that.
- */
- if (pFormat->container == drwav_container_riff) {
- if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) {
- return DRWAV_FALSE; /* Not enough room to store every sample. */
- }
- }
- }
-
- pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
-
-
- /* "RIFF" chunk. */
- if (pFormat->container == drwav_container_riff) {
- drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize; /* +28 = "WAVE" + [sizeof "fmt " chunk] */
- runningPos += drwav__write(pWav, "RIFF", 4);
- runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF);
- runningPos += drwav__write(pWav, "WAVE", 4);
- } else if (pFormat->container == drwav_container_w64) {
- drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */
- runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16);
- runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF);
- runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16);
- } else if (pFormat->container == drwav_container_rf64) {
- runningPos += drwav__write(pWav, "RF64", 4);
- runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); /* Always 0xFFFFFFFF for RF64. Set to a proper value in the "ds64" chunk. */
- runningPos += drwav__write(pWav, "WAVE", 4);
- } else {
- return DRWAV_FALSE; /* Container not supported for writing. */
- }
-
-
- /* "ds64" chunk (RF64 only). */
- if (pFormat->container == drwav_container_rf64) {
- drwav_uint32 initialds64ChunkSize = 28; /* 28 = [Size of RIFF (8 bytes)] + [Size of DATA (8 bytes)] + [Sample Count (8 bytes)] + [Table Length (4 bytes)]. Table length always set to 0. */
- drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; /* +8 for the ds64 header. */
-
- runningPos += drwav__write(pWav, "ds64", 4);
- runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize); /* Size of ds64. */
- runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize); /* Size of RIFF. Set to true value at the end. */
- runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize); /* Size of DATA. Set to true value at the end. */
- runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount); /* Sample count. */
- runningPos += drwav__write_u32ne_to_le(pWav, 0); /* Table length. Always set to zero in our case since we're not doing any other chunks than "DATA". */
- }
-
-
- /* "fmt " chunk. */
- if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) {
- chunkSizeFMT = 16;
- runningPos += drwav__write(pWav, "fmt ", 4);
- runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT);
- } else if (pFormat->container == drwav_container_w64) {
- chunkSizeFMT = 40;
- runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16);
- runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT);
- }
-
- runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag);
- runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels);
- runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate);
- runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec);
- runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign);
- runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample);
-
- /* TODO: is a 'fact' chunk required for DR_WAVE_FORMAT_IEEE_FLOAT? */
-
- if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64)) {
- runningPos += drwav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount);
- }
-
- pWav->dataChunkDataPos = runningPos;
-
- /* "data" chunk. */
- if (pFormat->container == drwav_container_riff) {
- drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize;
- runningPos += drwav__write(pWav, "data", 4);
- runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA);
- } else if (pFormat->container == drwav_container_w64) {
- drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */
- runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16);
- runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA);
- } else if (pFormat->container == drwav_container_rf64) {
- runningPos += drwav__write(pWav, "data", 4);
- runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); /* Always set to 0xFFFFFFFF for RF64. The true size of the data chunk is specified in the ds64 chunk. */
- }
-
- /* Set some properties for the client's convenience. */
- pWav->container = pFormat->container;
- pWav->channels = (drwav_uint16)pFormat->channels;
- pWav->sampleRate = pFormat->sampleRate;
- pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
- pWav->translatedFormatTag = (drwav_uint16)pFormat->format;
- pWav->dataChunkDataPos = runningPos;
-
- return DRWAV_TRUE;
-}
-
-
-DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
- return DRWAV_FALSE;
- }
-
- return drwav_init_write__internal(pWav, pFormat, 0); /* DRWAV_FALSE = Not Sequential */
-}
-
-DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) {
- return DRWAV_FALSE;
- }
-
- return drwav_init_write__internal(pWav, pFormat, totalSampleCount); /* DRWAV_TRUE = Sequential */
-}
-
-DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (pFormat == NULL) {
- return DRWAV_FALSE;
- }
-
- return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks);
-}
-
-DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
-{
- if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
- return DRWAV_FALSE;
- }
-
- pWav->pMetadata = pMetadata;
- pWav->metadataCount = metadataCount;
-
- return drwav_init_write__internal(pWav, pFormat, 0);
-}
-
-
-DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount)
-{
- /* Casting totalFrameCount to drwav_int64 for VC6 compatibility. No issues in practice because nobody is going to exhaust the whole 63 bits. */
- drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0);
- drwav_uint64 riffChunkSizeBytes;
- drwav_uint64 fileSizeBytes = 0;
-
- if (pFormat->container == drwav_container_riff) {
- riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount);
- fileSizeBytes = (8 + riffChunkSizeBytes); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */
- } else if (pFormat->container == drwav_container_w64) {
- riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes);
- fileSizeBytes = riffChunkSizeBytes;
- } else if (pFormat->container == drwav_container_rf64) {
- riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount);
- fileSizeBytes = (8 + riffChunkSizeBytes); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */
- }
-
- return fileSizeBytes;
-}
-
-
-#ifndef DR_WAV_NO_STDIO
-
-/* Errno */
-/* drwav_result_from_errno() is only used for fopen() and wfopen() so putting it inside DR_WAV_NO_STDIO for now. If something else needs this later we can move it out. */
-#include <errno.h>
-DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e)
-{
- switch (e)
- {
- case 0: return DRWAV_SUCCESS;
- #ifdef EPERM
- case EPERM: return DRWAV_INVALID_OPERATION;
- #endif
- #ifdef ENOENT
- case ENOENT: return DRWAV_DOES_NOT_EXIST;
- #endif
- #ifdef ESRCH
- case ESRCH: return DRWAV_DOES_NOT_EXIST;
- #endif
- #ifdef EINTR
- case EINTR: return DRWAV_INTERRUPT;
- #endif
- #ifdef EIO
- case EIO: return DRWAV_IO_ERROR;
- #endif
- #ifdef ENXIO
- case ENXIO: return DRWAV_DOES_NOT_EXIST;
- #endif
- #ifdef E2BIG
- case E2BIG: return DRWAV_INVALID_ARGS;
- #endif
- #ifdef ENOEXEC
- case ENOEXEC: return DRWAV_INVALID_FILE;
- #endif
- #ifdef EBADF
- case EBADF: return DRWAV_INVALID_FILE;
- #endif
- #ifdef ECHILD
- case ECHILD: return DRWAV_ERROR;
- #endif
- #ifdef EAGAIN
- case EAGAIN: return DRWAV_UNAVAILABLE;
- #endif
- #ifdef ENOMEM
- case ENOMEM: return DRWAV_OUT_OF_MEMORY;
- #endif
- #ifdef EACCES
- case EACCES: return DRWAV_ACCESS_DENIED;
- #endif
- #ifdef EFAULT
- case EFAULT: return DRWAV_BAD_ADDRESS;
- #endif
- #ifdef ENOTBLK
- case ENOTBLK: return DRWAV_ERROR;
- #endif
- #ifdef EBUSY
- case EBUSY: return DRWAV_BUSY;
- #endif
- #ifdef EEXIST
- case EEXIST: return DRWAV_ALREADY_EXISTS;
- #endif
- #ifdef EXDEV
- case EXDEV: return DRWAV_ERROR;
- #endif
- #ifdef ENODEV
- case ENODEV: return DRWAV_DOES_NOT_EXIST;
- #endif
- #ifdef ENOTDIR
- case ENOTDIR: return DRWAV_NOT_DIRECTORY;
- #endif
- #ifdef EISDIR
- case EISDIR: return DRWAV_IS_DIRECTORY;
- #endif
- #ifdef EINVAL
- case EINVAL: return DRWAV_INVALID_ARGS;
- #endif
- #ifdef ENFILE
- case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES;
- #endif
- #ifdef EMFILE
- case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES;
- #endif
- #ifdef ENOTTY
- case ENOTTY: return DRWAV_INVALID_OPERATION;
- #endif
- #ifdef ETXTBSY
- case ETXTBSY: return DRWAV_BUSY;
- #endif
- #ifdef EFBIG
- case EFBIG: return DRWAV_TOO_BIG;
- #endif
- #ifdef ENOSPC
- case ENOSPC: return DRWAV_NO_SPACE;
- #endif
- #ifdef ESPIPE
- case ESPIPE: return DRWAV_BAD_SEEK;
- #endif
- #ifdef EROFS
- case EROFS: return DRWAV_ACCESS_DENIED;
- #endif
- #ifdef EMLINK
- case EMLINK: return DRWAV_TOO_MANY_LINKS;
- #endif
- #ifdef EPIPE
- case EPIPE: return DRWAV_BAD_PIPE;
- #endif
- #ifdef EDOM
- case EDOM: return DRWAV_OUT_OF_RANGE;
- #endif
- #ifdef ERANGE
- case ERANGE: return DRWAV_OUT_OF_RANGE;
- #endif
- #ifdef EDEADLK
- case EDEADLK: return DRWAV_DEADLOCK;
- #endif
- #ifdef ENAMETOOLONG
- case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG;
- #endif
- #ifdef ENOLCK
- case ENOLCK: return DRWAV_ERROR;
- #endif
- #ifdef ENOSYS
- case ENOSYS: return DRWAV_NOT_IMPLEMENTED;
- #endif
- #ifdef ENOTEMPTY
- case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY;
- #endif
- #ifdef ELOOP
- case ELOOP: return DRWAV_TOO_MANY_LINKS;
- #endif
- #ifdef ENOMSG
- case ENOMSG: return DRWAV_NO_MESSAGE;
- #endif
- #ifdef EIDRM
- case EIDRM: return DRWAV_ERROR;
- #endif
- #ifdef ECHRNG
- case ECHRNG: return DRWAV_ERROR;
- #endif
- #ifdef EL2NSYNC
- case EL2NSYNC: return DRWAV_ERROR;
- #endif
- #ifdef EL3HLT
- case EL3HLT: return DRWAV_ERROR;
- #endif
- #ifdef EL3RST
- case EL3RST: return DRWAV_ERROR;
- #endif
- #ifdef ELNRNG
- case ELNRNG: return DRWAV_OUT_OF_RANGE;
- #endif
- #ifdef EUNATCH
- case EUNATCH: return DRWAV_ERROR;
- #endif
- #ifdef ENOCSI
- case ENOCSI: return DRWAV_ERROR;
- #endif
- #ifdef EL2HLT
- case EL2HLT: return DRWAV_ERROR;
- #endif
- #ifdef EBADE
- case EBADE: return DRWAV_ERROR;
- #endif
- #ifdef EBADR
- case EBADR: return DRWAV_ERROR;
- #endif
- #ifdef EXFULL
- case EXFULL: return DRWAV_ERROR;
- #endif
- #ifdef ENOANO
- case ENOANO: return DRWAV_ERROR;
- #endif
- #ifdef EBADRQC
- case EBADRQC: return DRWAV_ERROR;
- #endif
- #ifdef EBADSLT
- case EBADSLT: return DRWAV_ERROR;
- #endif
- #ifdef EBFONT
- case EBFONT: return DRWAV_INVALID_FILE;
- #endif
- #ifdef ENOSTR
- case ENOSTR: return DRWAV_ERROR;
- #endif
- #ifdef ENODATA
- case ENODATA: return DRWAV_NO_DATA_AVAILABLE;
- #endif
- #ifdef ETIME
- case ETIME: return DRWAV_TIMEOUT;
- #endif
- #ifdef ENOSR
- case ENOSR: return DRWAV_NO_DATA_AVAILABLE;
- #endif
- #ifdef ENONET
- case ENONET: return DRWAV_NO_NETWORK;
- #endif
- #ifdef ENOPKG
- case ENOPKG: return DRWAV_ERROR;
- #endif
- #ifdef EREMOTE
- case EREMOTE: return DRWAV_ERROR;
- #endif
- #ifdef ENOLINK
- case ENOLINK: return DRWAV_ERROR;
- #endif
- #ifdef EADV
- case EADV: return DRWAV_ERROR;
- #endif
- #ifdef ESRMNT
- case ESRMNT: return DRWAV_ERROR;
- #endif
- #ifdef ECOMM
- case ECOMM: return DRWAV_ERROR;
- #endif
- #ifdef EPROTO
- case EPROTO: return DRWAV_ERROR;
- #endif
- #ifdef EMULTIHOP
- case EMULTIHOP: return DRWAV_ERROR;
- #endif
- #ifdef EDOTDOT
- case EDOTDOT: return DRWAV_ERROR;
- #endif
- #ifdef EBADMSG
- case EBADMSG: return DRWAV_BAD_MESSAGE;
- #endif
- #ifdef EOVERFLOW
- case EOVERFLOW: return DRWAV_TOO_BIG;
- #endif
- #ifdef ENOTUNIQ
- case ENOTUNIQ: return DRWAV_NOT_UNIQUE;
- #endif
- #ifdef EBADFD
- case EBADFD: return DRWAV_ERROR;
- #endif
- #ifdef EREMCHG
- case EREMCHG: return DRWAV_ERROR;
- #endif
- #ifdef ELIBACC
- case ELIBACC: return DRWAV_ACCESS_DENIED;
- #endif
- #ifdef ELIBBAD
- case ELIBBAD: return DRWAV_INVALID_FILE;
- #endif
- #ifdef ELIBSCN
- case ELIBSCN: return DRWAV_INVALID_FILE;
- #endif
- #ifdef ELIBMAX
- case ELIBMAX: return DRWAV_ERROR;
- #endif
- #ifdef ELIBEXEC
- case ELIBEXEC: return DRWAV_ERROR;
- #endif
- #ifdef EILSEQ
- case EILSEQ: return DRWAV_INVALID_DATA;
- #endif
- #ifdef ERESTART
- case ERESTART: return DRWAV_ERROR;
- #endif
- #ifdef ESTRPIPE
- case ESTRPIPE: return DRWAV_ERROR;
- #endif
- #ifdef EUSERS
- case EUSERS: return DRWAV_ERROR;
- #endif
- #ifdef ENOTSOCK
- case ENOTSOCK: return DRWAV_NOT_SOCKET;
- #endif
- #ifdef EDESTADDRREQ
- case EDESTADDRREQ: return DRWAV_NO_ADDRESS;
- #endif
- #ifdef EMSGSIZE
- case EMSGSIZE: return DRWAV_TOO_BIG;
- #endif
- #ifdef EPROTOTYPE
- case EPROTOTYPE: return DRWAV_BAD_PROTOCOL;
- #endif
- #ifdef ENOPROTOOPT
- case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE;
- #endif
- #ifdef EPROTONOSUPPORT
- case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED;
- #endif
- #ifdef ESOCKTNOSUPPORT
- case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED;
- #endif
- #ifdef EOPNOTSUPP
- case EOPNOTSUPP: return DRWAV_INVALID_OPERATION;
- #endif
- #ifdef EPFNOSUPPORT
- case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED;
- #endif
- #ifdef EAFNOSUPPORT
- case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED;
- #endif
- #ifdef EADDRINUSE
- case EADDRINUSE: return DRWAV_ALREADY_IN_USE;
- #endif
- #ifdef EADDRNOTAVAIL
- case EADDRNOTAVAIL: return DRWAV_ERROR;
- #endif
- #ifdef ENETDOWN
- case ENETDOWN: return DRWAV_NO_NETWORK;
- #endif
- #ifdef ENETUNREACH
- case ENETUNREACH: return DRWAV_NO_NETWORK;
- #endif
- #ifdef ENETRESET
- case ENETRESET: return DRWAV_NO_NETWORK;
- #endif
- #ifdef ECONNABORTED
- case ECONNABORTED: return DRWAV_NO_NETWORK;
- #endif
- #ifdef ECONNRESET
- case ECONNRESET: return DRWAV_CONNECTION_RESET;
- #endif
- #ifdef ENOBUFS
- case ENOBUFS: return DRWAV_NO_SPACE;
- #endif
- #ifdef EISCONN
- case EISCONN: return DRWAV_ALREADY_CONNECTED;
- #endif
- #ifdef ENOTCONN
- case ENOTCONN: return DRWAV_NOT_CONNECTED;
- #endif
- #ifdef ESHUTDOWN
- case ESHUTDOWN: return DRWAV_ERROR;
- #endif
- #ifdef ETOOMANYREFS
- case ETOOMANYREFS: return DRWAV_ERROR;
- #endif
- #ifdef ETIMEDOUT
- case ETIMEDOUT: return DRWAV_TIMEOUT;
- #endif
- #ifdef ECONNREFUSED
- case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED;
- #endif
- #ifdef EHOSTDOWN
- case EHOSTDOWN: return DRWAV_NO_HOST;
- #endif
- #ifdef EHOSTUNREACH
- case EHOSTUNREACH: return DRWAV_NO_HOST;
- #endif
- #ifdef EALREADY
- case EALREADY: return DRWAV_IN_PROGRESS;
- #endif
- #ifdef EINPROGRESS
- case EINPROGRESS: return DRWAV_IN_PROGRESS;
- #endif
- #ifdef ESTALE
- case ESTALE: return DRWAV_INVALID_FILE;
- #endif
- #ifdef EUCLEAN
- case EUCLEAN: return DRWAV_ERROR;
- #endif
- #ifdef ENOTNAM
- case ENOTNAM: return DRWAV_ERROR;
- #endif
- #ifdef ENAVAIL
- case ENAVAIL: return DRWAV_ERROR;
- #endif
- #ifdef EISNAM
- case EISNAM: return DRWAV_ERROR;
- #endif
- #ifdef EREMOTEIO
- case EREMOTEIO: return DRWAV_IO_ERROR;
- #endif
- #ifdef EDQUOT
- case EDQUOT: return DRWAV_NO_SPACE;
- #endif
- #ifdef ENOMEDIUM
- case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST;
- #endif
- #ifdef EMEDIUMTYPE
- case EMEDIUMTYPE: return DRWAV_ERROR;
- #endif
- #ifdef ECANCELED
- case ECANCELED: return DRWAV_CANCELLED;
- #endif
- #ifdef ENOKEY
- case ENOKEY: return DRWAV_ERROR;
- #endif
- #ifdef EKEYEXPIRED
- case EKEYEXPIRED: return DRWAV_ERROR;
- #endif
- #ifdef EKEYREVOKED
- case EKEYREVOKED: return DRWAV_ERROR;
- #endif
- #ifdef EKEYREJECTED
- case EKEYREJECTED: return DRWAV_ERROR;
- #endif
- #ifdef EOWNERDEAD
- case EOWNERDEAD: return DRWAV_ERROR;
- #endif
- #ifdef ENOTRECOVERABLE
- case ENOTRECOVERABLE: return DRWAV_ERROR;
- #endif
- #ifdef ERFKILL
- case ERFKILL: return DRWAV_ERROR;
- #endif
- #ifdef EHWPOISON
- case EHWPOISON: return DRWAV_ERROR;
- #endif
- default: return DRWAV_ERROR;
- }
-}
-/* End Errno */
-
-/* fopen */
-DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
-{
-#if defined(_MSC_VER) && _MSC_VER >= 1400
- errno_t err;
-#endif
-
- if (ppFile != NULL) {
- *ppFile = NULL; /* Safety. */
- }
-
- if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
- return DRWAV_INVALID_ARGS;
- }
-
-#if defined(_MSC_VER) && _MSC_VER >= 1400
- err = fopen_s(ppFile, pFilePath, pOpenMode);
- if (err != 0) {
- return drwav_result_from_errno(err);
- }
-#else
-#if defined(_WIN32) || defined(__APPLE__)
- *ppFile = fopen(pFilePath, pOpenMode);
-#else
- #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
- *ppFile = fopen64(pFilePath, pOpenMode);
- #else
- *ppFile = fopen(pFilePath, pOpenMode);
- #endif
-#endif
- if (*ppFile == NULL) {
- drwav_result result = drwav_result_from_errno(errno);
- if (result == DRWAV_SUCCESS) {
- result = DRWAV_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */
- }
-
- return result;
- }
-#endif
-
- return DRWAV_SUCCESS;
-}
-
-/*
-_wfopen() isn't always available in all compilation environments.
-
- * Windows only.
- * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
- * MinGW-64 (both 32- and 64-bit) seems to support it.
- * MinGW wraps it in !defined(__STRICT_ANSI__).
- * OpenWatcom wraps it in !defined(_NO_EXT_KEYS).
-
-This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()
-fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.
-*/
-#if defined(_WIN32)
- #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
- #define DRWAV_HAS_WFOPEN
- #endif
-#endif
-
-#ifndef DR_WAV_NO_WCHAR
-DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (ppFile != NULL) {
- *ppFile = NULL; /* Safety. */
- }
-
- if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
- return DRWAV_INVALID_ARGS;
- }
-
-#if defined(DRWAV_HAS_WFOPEN)
- {
- /* Use _wfopen() on Windows. */
- #if defined(_MSC_VER) && _MSC_VER >= 1400
- errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
- if (err != 0) {
- return drwav_result_from_errno(err);
- }
- #else
- *ppFile = _wfopen(pFilePath, pOpenMode);
- if (*ppFile == NULL) {
- return drwav_result_from_errno(errno);
- }
- #endif
- (void)pAllocationCallbacks;
- }
-#else
- /*
- Use fopen() on anything other than Windows. Requires a conversion. This is annoying because
- fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note
- that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for
- maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler
- error I'll look into improving compatibility.
- */
-
- /*
- Some compilers don't support wchar_t or wcsrtombs() which we're using below. In this case we just
- need to abort with an error. If you encounter a compiler lacking such support, add it to this list
- and submit a bug report and it'll be added to the library upstream.
- */
- #if defined(__DJGPP__)
- {
- /* Nothing to do here. This will fall through to the error check below. */
- }
- #else
- {
- mbstate_t mbs;
- size_t lenMB;
- const wchar_t* pFilePathTemp = pFilePath;
- char* pFilePathMB = NULL;
- char pOpenModeMB[32] = {0};
-
- /* Get the length first. */
- DRWAV_ZERO_OBJECT(&mbs);
- lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
- if (lenMB == (size_t)-1) {
- return drwav_result_from_errno(errno);
- }
-
- pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
- if (pFilePathMB == NULL) {
- return DRWAV_OUT_OF_MEMORY;
- }
-
- pFilePathTemp = pFilePath;
- DRWAV_ZERO_OBJECT(&mbs);
- wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
-
- /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */
- {
- size_t i = 0;
- for (;;) {
- if (pOpenMode[i] == 0) {
- pOpenModeMB[i] = '\0';
- break;
- }
-
- pOpenModeMB[i] = (char)pOpenMode[i];
- i += 1;
- }
- }
-
- *ppFile = fopen(pFilePathMB, pOpenModeMB);
-
- drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
- }
- #endif
-
- if (*ppFile == NULL) {
- return DRWAV_ERROR;
- }
-#endif
-
- return DRWAV_SUCCESS;
-}
-#endif
-/* End fopen */
-
-
-DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
-{
- return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
-}
-
-DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
-{
- return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
-}
-
-DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
-{
- return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
-}
-
-DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
-}
-
-
-DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav_bool32 result;
-
- result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
- if (result != DRWAV_TRUE) {
- fclose(pFile);
- return result;
- }
-
- result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
- if (result != DRWAV_TRUE) {
- fclose(pFile);
- return result;
- }
-
- return DRWAV_TRUE;
-}
-
-DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- FILE* pFile;
- if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) {
- return DRWAV_FALSE;
- }
-
- /* This takes ownership of the FILE* object. */
- return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
-}
-
-#ifndef DR_WAV_NO_WCHAR
-DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
-}
-
-DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- FILE* pFile;
- if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) {
- return DRWAV_FALSE;
- }
-
- /* This takes ownership of the FILE* object. */
- return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
-}
-#endif
-
-DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- FILE* pFile;
- if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) {
- return DRWAV_FALSE;
- }
-
- /* This takes ownership of the FILE* object. */
- return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | DRWAV_WITH_METADATA, pAllocationCallbacks);
-}
-
-#ifndef DR_WAV_NO_WCHAR
-DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- FILE* pFile;
- if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) {
- return DRWAV_FALSE;
- }
-
- /* This takes ownership of the FILE* object. */
- return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | DRWAV_WITH_METADATA, pAllocationCallbacks);
-}
-#endif
-
-
-DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav_bool32 result;
-
- result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
- if (result != DRWAV_TRUE) {
- fclose(pFile);
- return result;
- }
-
- result = drwav_init_write__internal(pWav, pFormat, totalSampleCount);
- if (result != DRWAV_TRUE) {
- fclose(pFile);
- return result;
- }
-
- return DRWAV_TRUE;
-}
-
-DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- FILE* pFile;
- if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) {
- return DRWAV_FALSE;
- }
-
- /* This takes ownership of the FILE* object. */
- return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
-}
-
-#ifndef DR_WAV_NO_WCHAR
-DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- FILE* pFile;
- if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) {
- return DRWAV_FALSE;
- }
-
- /* This takes ownership of the FILE* object. */
- return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
-}
-#endif
-
-DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
-}
-
-DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
-}
-
-DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (pFormat == NULL) {
- return DRWAV_FALSE;
- }
-
- return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
-}
-
-#ifndef DR_WAV_NO_WCHAR
-DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
-}
-
-DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
-}
-
-DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (pFormat == NULL) {
- return DRWAV_FALSE;
- }
-
- return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
-}
-#endif
-#endif /* DR_WAV_NO_STDIO */
-
-
-DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
-{
- drwav* pWav = (drwav*)pUserData;
- size_t bytesRemaining;
-
- DRWAV_ASSERT(pWav != NULL);
- DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos);
-
- bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos;
- if (bytesToRead > bytesRemaining) {
- bytesToRead = bytesRemaining;
- }
-
- if (bytesToRead > 0) {
- DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead);
- pWav->memoryStream.currentReadPos += bytesToRead;
- }
-
- return bytesToRead;
-}
-
-DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin)
-{
- drwav* pWav = (drwav*)pUserData;
- DRWAV_ASSERT(pWav != NULL);
-
- if (origin == drwav_seek_origin_current) {
- if (offset > 0) {
- if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) {
- return DRWAV_FALSE; /* Trying to seek too far forward. */
- }
- } else {
- if (pWav->memoryStream.currentReadPos < (size_t)-offset) {
- return DRWAV_FALSE; /* Trying to seek too far backwards. */
- }
- }
-
- /* This will never underflow thanks to the clamps above. */
- pWav->memoryStream.currentReadPos += offset;
- } else {
- if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) {
- pWav->memoryStream.currentReadPos = offset;
- } else {
- return DRWAV_FALSE; /* Trying to seek too far forward. */
- }
- }
-
- return DRWAV_TRUE;
-}
-
-DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
-{
- drwav* pWav = (drwav*)pUserData;
- size_t bytesRemaining;
-
- DRWAV_ASSERT(pWav != NULL);
- DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos);
-
- bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos;
- if (bytesRemaining < bytesToWrite) {
- /* Need to reallocate. */
- void* pNewData;
- size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2;
-
- /* If doubling wasn't enough, just make it the minimum required size to write the data. */
- if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) {
- newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite;
- }
-
- pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks);
- if (pNewData == NULL) {
- return 0;
- }
-
- *pWav->memoryStreamWrite.ppData = pNewData;
- pWav->memoryStreamWrite.dataCapacity = newDataCapacity;
- }
-
- DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite);
-
- pWav->memoryStreamWrite.currentWritePos += bytesToWrite;
- if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) {
- pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos;
- }
-
- *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize;
-
- return bytesToWrite;
-}
-
-DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)
-{
- drwav* pWav = (drwav*)pUserData;
- DRWAV_ASSERT(pWav != NULL);
-
- if (origin == drwav_seek_origin_current) {
- if (offset > 0) {
- if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) {
- offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); /* Trying to seek too far forward. */
- }
- } else {
- if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) {
- offset = -(int)pWav->memoryStreamWrite.currentWritePos; /* Trying to seek too far backwards. */
- }
- }
-
- /* This will never underflow thanks to the clamps above. */
- pWav->memoryStreamWrite.currentWritePos += offset;
- } else {
- if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) {
- pWav->memoryStreamWrite.currentWritePos = offset;
- } else {
- pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; /* Trying to seek too far forward. */
- }
- }
-
- return DRWAV_TRUE;
-}
-
-DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks);
-}
-
-DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (data == NULL || dataSize == 0) {
- return DRWAV_FALSE;
- }
-
- if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
- return DRWAV_FALSE;
- }
-
- pWav->memoryStream.data = (const drwav_uint8*)data;
- pWav->memoryStream.dataSize = dataSize;
- pWav->memoryStream.currentReadPos = 0;
-
- return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
-}
-
-DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (data == NULL || dataSize == 0) {
- return DRWAV_FALSE;
- }
-
- if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
- return DRWAV_FALSE;
- }
-
- pWav->memoryStream.data = (const drwav_uint8*)data;
- pWav->memoryStream.dataSize = dataSize;
- pWav->memoryStream.currentReadPos = 0;
-
- return drwav_init__internal(pWav, NULL, NULL, flags | DRWAV_WITH_METADATA);
-}
-
-
-DRWAV_PRIVATE drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (ppData == NULL || pDataSize == NULL) {
- return DRWAV_FALSE;
- }
-
- *ppData = NULL; /* Important because we're using realloc()! */
- *pDataSize = 0;
-
- if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) {
- return DRWAV_FALSE;
- }
-
- pWav->memoryStreamWrite.ppData = ppData;
- pWav->memoryStreamWrite.pDataSize = pDataSize;
- pWav->memoryStreamWrite.dataSize = 0;
- pWav->memoryStreamWrite.dataCapacity = 0;
- pWav->memoryStreamWrite.currentWritePos = 0;
-
- return drwav_init_write__internal(pWav, pFormat, totalSampleCount);
-}
-
-DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
-}
-
-DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
-}
-
-DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (pFormat == NULL) {
- return DRWAV_FALSE;
- }
-
- return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
-}
-
-
-
-DRWAV_API drwav_result drwav_uninit(drwav* pWav)
-{
- drwav_result result = DRWAV_SUCCESS;
-
- if (pWav == NULL) {
- return DRWAV_INVALID_ARGS;
- }
-
- /*
- If the drwav object was opened in write mode we'll need to finalize a few things:
- - Make sure the "data" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers.
- - Set the size of the "data" chunk.
- */
- if (pWav->onWrite != NULL) {
- drwav_uint32 paddingSize = 0;
-
- /* Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding. */
- if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) {
- paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize);
- } else {
- paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize);
- }
-
- if (paddingSize > 0) {
- drwav_uint64 paddingData = 0;
- drwav__write(pWav, &paddingData, paddingSize); /* Byte order does not matter for this. */
- }
-
- /*
- Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need
- to do this when using non-sequential mode.
- */
- if (pWav->onSeek && !pWav->isSequentialWrite) {
- if (pWav->container == drwav_container_riff) {
- /* The "RIFF" chunk size. */
- if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
- drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
- drwav__write_u32ne_to_le(pWav, riffChunkSize);
- }
-
- /* The "data" chunk size. */
- if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) {
- drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize);
- drwav__write_u32ne_to_le(pWav, dataChunkSize);
- }
- } else if (pWav->container == drwav_container_w64) {
- /* The "RIFF" chunk size. */
- if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
- drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize);
- drwav__write_u64ne_to_le(pWav, riffChunkSize);
- }
-
- /* The "data" chunk size. */
- if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) {
- drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize);
- drwav__write_u64ne_to_le(pWav, dataChunkSize);
- }
- } else if (pWav->container == drwav_container_rf64) {
- /* We only need to update the ds64 chunk. The "RIFF" and "data" chunks always have their sizes set to 0xFFFFFFFF for RF64. */
- int ds64BodyPos = 12 + 8;
-
- /* The "RIFF" chunk size. */
- if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) {
- drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
- drwav__write_u64ne_to_le(pWav, riffChunkSize);
- }
-
- /* The "data" chunk size. */
- if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) {
- drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize);
- drwav__write_u64ne_to_le(pWav, dataChunkSize);
- }
- }
- }
-
- /* Validation for sequential mode. */
- if (pWav->isSequentialWrite) {
- if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) {
- result = DRWAV_INVALID_FILE;
- }
- }
- } else {
- drwav_free(pWav->pMetadata, &pWav->allocationCallbacks);
- }
-
-#ifndef DR_WAV_NO_STDIO
- /*
- If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file()
- was used by looking at the onRead and onSeek callbacks.
- */
- if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) {
- fclose((FILE*)pWav->pUserData);
- }
-#endif
-
- return result;
-}
-
-
-
-DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)
-{
- size_t bytesRead;
- drwav_uint32 bytesPerFrame;
-
- if (pWav == NULL || bytesToRead == 0) {
- return 0; /* Invalid args. */
- }
-
- if (bytesToRead > pWav->bytesRemaining) {
- bytesToRead = (size_t)pWav->bytesRemaining;
- }
-
- if (bytesToRead == 0) {
- return 0; /* At end. */
- }
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0; /* Could not determine the bytes per frame. */
- }
-
- if (pBufferOut != NULL) {
- bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
- } else {
- /* We need to seek. If we fail, we need to read-and-discard to make sure we get a good byte count. */
- bytesRead = 0;
- while (bytesRead < bytesToRead) {
- size_t bytesToSeek = (bytesToRead - bytesRead);
- if (bytesToSeek > 0x7FFFFFFF) {
- bytesToSeek = 0x7FFFFFFF;
- }
-
- if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) {
- break;
- }
-
- bytesRead += bytesToSeek;
- }
-
- /* When we get here we may need to read-and-discard some data. */
- while (bytesRead < bytesToRead) {
- drwav_uint8 buffer[4096];
- size_t bytesSeeked;
- size_t bytesToSeek = (bytesToRead - bytesRead);
- if (bytesToSeek > sizeof(buffer)) {
- bytesToSeek = sizeof(buffer);
- }
-
- bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek);
- bytesRead += bytesSeeked;
-
- if (bytesSeeked < bytesToSeek) {
- break; /* Reached the end. */
- }
- }
- }
-
- pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame;
-
- pWav->bytesRemaining -= bytesRead;
- return bytesRead;
-}
-
-
-
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
-{
- drwav_uint32 bytesPerFrame;
- drwav_uint64 bytesToRead; /* Intentionally uint64 instead of size_t so we can do a check that we're not reading too much on 32-bit builds. */
- drwav_uint64 framesRemainingInFile;
-
- if (pWav == NULL || framesToRead == 0) {
- return 0;
- }
-
- /* Cannot use this function for compressed formats. */
- if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
- return 0;
- }
-
- framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames;
- if (framesToRead > framesRemainingInFile) {
- framesToRead = framesRemainingInFile;
- }
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0;
- }
-
- /* Don't try to read more samples than can potentially fit in the output buffer. */
- bytesToRead = framesToRead * bytesPerFrame;
- if (bytesToRead > DRWAV_SIZE_MAX) {
- bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame; /* Round the number of bytes to read to a clean frame boundary. */
- }
-
- /*
- Doing an explicit check here just to make it clear that we don't want to be attempt to read anything if there's no bytes to read. There
- *could* be a time where it evaluates to 0 due to overflowing.
- */
- if (bytesToRead == 0) {
- return 0;
- }
-
- return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame;
-}
-
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
-{
- drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
-
- if (pBufferOut != NULL) {
- drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0; /* Could not get the bytes per frame which means bytes per sample cannot be determined and we don't know how to byte swap. */
- }
-
- drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels);
- }
-
- return framesRead;
-}
-
-DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
-{
- drwav_uint64 framesRead = 0;
-
- if (drwav_is_container_be(pWav->container)) {
- /*
- Special case for AIFF. AIFF is a big-endian encoded format, but it supports a format that is
- PCM in little-endian encoding. In this case, we fall through this branch and treate it as
- little-endian.
- */
- if (pWav->container != drwav_container_aiff || pWav->aiff.isLE == DRWAV_FALSE) {
- if (drwav__is_little_endian()) {
- framesRead = drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
- } else {
- framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
- }
-
- goto post_process;
- }
- }
-
- /* Getting here means the data should be considered little-endian. */
- if (drwav__is_little_endian()) {
- framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
- } else {
- framesRead = drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
- }
-
- /*
- Here is where we check if we need to do a signed/unsigned conversion for AIFF. The reason we need to do this
- is because dr_wav always assumes an 8-bit sample is unsigned, whereas AIFF can have signed 8-bit formats.
- */
- post_process:
- {
- if (pWav->container == drwav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == DRWAV_FALSE) {
- if (pBufferOut != NULL) {
- drwav_uint64 iSample;
-
- for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) {
- ((drwav_uint8*)pBufferOut)[iSample] += 128;
- }
- }
- }
- }
-
- return framesRead;
-}
-
-
-
-DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav)
-{
- if (pWav->onWrite != NULL) {
- return DRWAV_FALSE; /* No seeking in write mode. */
- }
-
- if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) {
- return DRWAV_FALSE;
- }
-
- if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
- /* Cached data needs to be cleared for compressed formats. */
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
- DRWAV_ZERO_OBJECT(&pWav->msadpcm);
- } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
- DRWAV_ZERO_OBJECT(&pWav->ima);
- } else {
- DRWAV_ASSERT(DRWAV_FALSE); /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */
- }
- }
-
- pWav->readCursorInPCMFrames = 0;
- pWav->bytesRemaining = pWav->dataChunkDataSize;
-
- return DRWAV_TRUE;
-}
-
-DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex)
-{
- /* Seeking should be compatible with wave files > 2GB. */
-
- if (pWav == NULL || pWav->onSeek == NULL) {
- return DRWAV_FALSE;
- }
-
- /* No seeking in write mode. */
- if (pWav->onWrite != NULL) {
- return DRWAV_FALSE;
- }
-
- /* If there are no samples, just return DRWAV_TRUE without doing anything. */
- if (pWav->totalPCMFrameCount == 0) {
- return DRWAV_TRUE;
- }
-
- /* Make sure the sample is clamped. */
- if (targetFrameIndex > pWav->totalPCMFrameCount) {
- targetFrameIndex = pWav->totalPCMFrameCount;
- }
-
- /*
- For compressed formats we just use a slow generic seek. If we are seeking forward we just seek forward. If we are going backwards we need
- to seek back to the start.
- */
- if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
- /* TODO: This can be optimized. */
-
- /*
- If we're seeking forward it's simple - just keep reading samples until we hit the sample we're requesting. If we're seeking backwards,
- we first need to seek back to the start and then just do the same thing as a forward seek.
- */
- if (targetFrameIndex < pWav->readCursorInPCMFrames) {
- if (!drwav_seek_to_first_pcm_frame(pWav)) {
- return DRWAV_FALSE;
- }
- }
-
- if (targetFrameIndex > pWav->readCursorInPCMFrames) {
- drwav_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames;
-
- drwav_int16 devnull[2048];
- while (offsetInFrames > 0) {
- drwav_uint64 framesRead = 0;
- drwav_uint64 framesToRead = offsetInFrames;
- if (framesToRead > drwav_countof(devnull)/pWav->channels) {
- framesToRead = drwav_countof(devnull)/pWav->channels;
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
- framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull);
- } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
- framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull);
- } else {
- DRWAV_ASSERT(DRWAV_FALSE); /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */
- }
-
- if (framesRead != framesToRead) {
- return DRWAV_FALSE;
- }
-
- offsetInFrames -= framesRead;
- }
- }
- } else {
- drwav_uint64 totalSizeInBytes;
- drwav_uint64 currentBytePos;
- drwav_uint64 targetBytePos;
- drwav_uint64 offset;
- drwav_uint32 bytesPerFrame;
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return DRWAV_FALSE; /* Not able to calculate offset. */
- }
-
- totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame;
- /*DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining);*/
-
- currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
- targetBytePos = targetFrameIndex * bytesPerFrame;
-
- if (currentBytePos < targetBytePos) {
- /* Offset forwards. */
- offset = (targetBytePos - currentBytePos);
- } else {
- /* Offset backwards. */
- if (!drwav_seek_to_first_pcm_frame(pWav)) {
- return DRWAV_FALSE;
- }
- offset = targetBytePos;
- }
-
- while (offset > 0) {
- int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
- if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) {
- return DRWAV_FALSE;
- }
-
- pWav->readCursorInPCMFrames += offset32 / bytesPerFrame;
- pWav->bytesRemaining -= offset32;
- offset -= offset32;
- }
- }
-
- return DRWAV_TRUE;
-}
-
-DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor)
-{
- if (pCursor == NULL) {
- return DRWAV_INVALID_ARGS;
- }
-
- *pCursor = 0; /* Safety. */
-
- if (pWav == NULL) {
- return DRWAV_INVALID_ARGS;
- }
-
- *pCursor = pWav->readCursorInPCMFrames;
-
- return DRWAV_SUCCESS;
-}
-
-DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength)
-{
- if (pLength == NULL) {
- return DRWAV_INVALID_ARGS;
- }
-
- *pLength = 0; /* Safety. */
-
- if (pWav == NULL) {
- return DRWAV_INVALID_ARGS;
- }
-
- *pLength = pWav->totalPCMFrameCount;
-
- return DRWAV_SUCCESS;
-}
-
-
-DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)
-{
- size_t bytesWritten;
-
- if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
- return 0;
- }
-
- bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
- pWav->dataChunkDataSize += bytesWritten;
-
- return bytesWritten;
-}
-
-DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
-{
- drwav_uint64 bytesToWrite;
- drwav_uint64 bytesWritten;
- const drwav_uint8* pRunningData;
-
- if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
- return 0;
- }
-
- bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
- if (bytesToWrite > DRWAV_SIZE_MAX) {
- return 0;
- }
-
- bytesWritten = 0;
- pRunningData = (const drwav_uint8*)pData;
-
- while (bytesToWrite > 0) {
- size_t bytesJustWritten;
- drwav_uint64 bytesToWriteThisIteration;
-
- bytesToWriteThisIteration = bytesToWrite;
- DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); /* <-- This is checked above. */
-
- bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
- if (bytesJustWritten == 0) {
- break;
- }
-
- bytesToWrite -= bytesJustWritten;
- bytesWritten += bytesJustWritten;
- pRunningData += bytesJustWritten;
- }
-
- return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
-}
-
-DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
-{
- drwav_uint64 bytesToWrite;
- drwav_uint64 bytesWritten;
- drwav_uint32 bytesPerSample;
- const drwav_uint8* pRunningData;
-
- if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
- return 0;
- }
-
- bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
- if (bytesToWrite > DRWAV_SIZE_MAX) {
- return 0;
- }
-
- bytesWritten = 0;
- pRunningData = (const drwav_uint8*)pData;
-
- bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels;
- if (bytesPerSample == 0) {
- return 0; /* Cannot determine bytes per sample, or bytes per sample is less than one byte. */
- }
-
- while (bytesToWrite > 0) {
- drwav_uint8 temp[4096];
- drwav_uint32 sampleCount;
- size_t bytesJustWritten;
- drwav_uint64 bytesToWriteThisIteration;
-
- bytesToWriteThisIteration = bytesToWrite;
- DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); /* <-- This is checked above. */
-
- /*
- WAV files are always little-endian. We need to byte swap on big-endian architectures. Since our input buffer is read-only we need
- to use an intermediary buffer for the conversion.
- */
- sampleCount = sizeof(temp)/bytesPerSample;
-
- if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) {
- bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample;
- }
-
- DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration);
- drwav__bswap_samples(temp, sampleCount, bytesPerSample);
-
- bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp);
- if (bytesJustWritten == 0) {
- break;
- }
-
- bytesToWrite -= bytesJustWritten;
- bytesWritten += bytesJustWritten;
- pRunningData += bytesJustWritten;
- }
-
- return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
-}
-
-DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
-{
- if (drwav__is_little_endian()) {
- return drwav_write_pcm_frames_le(pWav, framesToWrite, pData);
- } else {
- return drwav_write_pcm_frames_be(pWav, framesToWrite, pData);
- }
-}
-
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
-{
- drwav_uint64 totalFramesRead = 0;
-
- DRWAV_ASSERT(pWav != NULL);
- DRWAV_ASSERT(framesToRead > 0);
-
- /* TODO: Lots of room for optimization here. */
-
- while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
- DRWAV_ASSERT(framesToRead > 0); /* This loop iteration will never get hit with framesToRead == 0 because it's asserted at the top, and we check for 0 inside the loop just below. */
-
- /* If there are no cached frames we need to load a new block. */
- if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
- if (pWav->channels == 1) {
- /* Mono. */
- drwav_uint8 header[7];
- if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
- return totalFramesRead;
- }
- pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
-
- pWav->msadpcm.predictor[0] = header[0];
- pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 1);
- pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 3);
- pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 5);
- pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0];
- pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1];
- pWav->msadpcm.cachedFrameCount = 2;
- } else {
- /* Stereo. */
- drwav_uint8 header[14];
- if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
- return totalFramesRead;
- }
- pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
-
- pWav->msadpcm.predictor[0] = header[0];
- pWav->msadpcm.predictor[1] = header[1];
- pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 2);
- pWav->msadpcm.delta[1] = drwav_bytes_to_s16(header + 4);
- pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 6);
- pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav_bytes_to_s16(header + 8);
- pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 10);
- pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav_bytes_to_s16(header + 12);
-
- pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0];
- pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0];
- pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];
- pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];
- pWav->msadpcm.cachedFrameCount = 2;
- }
- }
-
- /* Output anything that's cached. */
- while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
- if (pBufferOut != NULL) {
- drwav_uint32 iSample = 0;
- for (iSample = 0; iSample < pWav->channels; iSample += 1) {
- pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample];
- }
-
- pBufferOut += pWav->channels;
- }
-
- framesToRead -= 1;
- totalFramesRead += 1;
- pWav->readCursorInPCMFrames += 1;
- pWav->msadpcm.cachedFrameCount -= 1;
- }
-
- if (framesToRead == 0) {
- break;
- }
-
-
- /*
- If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
- loop iteration which will trigger the loading of a new block.
- */
- if (pWav->msadpcm.cachedFrameCount == 0) {
- if (pWav->msadpcm.bytesRemainingInBlock == 0) {
- continue;
- } else {
- static drwav_int32 adaptationTable[] = {
- 230, 230, 230, 230, 307, 409, 512, 614,
- 768, 614, 512, 409, 307, 230, 230, 230
- };
- static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
- static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
-
- drwav_uint8 nibbles;
- drwav_int32 nibble0;
- drwav_int32 nibble1;
-
- if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
- return totalFramesRead;
- }
- pWav->msadpcm.bytesRemainingInBlock -= 1;
-
- /* TODO: Optimize away these if statements. */
- nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
- nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
-
- if (pWav->channels == 1) {
- /* Mono. */
- drwav_int32 newSample0;
- drwav_int32 newSample1;
-
- newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
- newSample0 += nibble0 * pWav->msadpcm.delta[0];
- newSample0 = drwav_clamp(newSample0, -32768, 32767);
-
- pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
- if (pWav->msadpcm.delta[0] < 16) {
- pWav->msadpcm.delta[0] = 16;
- }
-
- pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
- pWav->msadpcm.prevFrames[0][1] = newSample0;
-
-
- newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
- newSample1 += nibble1 * pWav->msadpcm.delta[0];
- newSample1 = drwav_clamp(newSample1, -32768, 32767);
-
- pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
- if (pWav->msadpcm.delta[0] < 16) {
- pWav->msadpcm.delta[0] = 16;
- }
-
- pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
- pWav->msadpcm.prevFrames[0][1] = newSample1;
-
-
- pWav->msadpcm.cachedFrames[2] = newSample0;
- pWav->msadpcm.cachedFrames[3] = newSample1;
- pWav->msadpcm.cachedFrameCount = 2;
- } else {
- /* Stereo. */
- drwav_int32 newSample0;
- drwav_int32 newSample1;
-
- /* Left. */
- newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
- newSample0 += nibble0 * pWav->msadpcm.delta[0];
- newSample0 = drwav_clamp(newSample0, -32768, 32767);
-
- pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
- if (pWav->msadpcm.delta[0] < 16) {
- pWav->msadpcm.delta[0] = 16;
- }
-
- pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
- pWav->msadpcm.prevFrames[0][1] = newSample0;
-
-
- /* Right. */
- newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
- newSample1 += nibble1 * pWav->msadpcm.delta[1];
- newSample1 = drwav_clamp(newSample1, -32768, 32767);
-
- pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
- if (pWav->msadpcm.delta[1] < 16) {
- pWav->msadpcm.delta[1] = 16;
- }
-
- pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
- pWav->msadpcm.prevFrames[1][1] = newSample1;
-
- pWav->msadpcm.cachedFrames[2] = newSample0;
- pWav->msadpcm.cachedFrames[3] = newSample1;
- pWav->msadpcm.cachedFrameCount = 1;
- }
- }
- }
- }
-
- return totalFramesRead;
-}
-
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
-{
- drwav_uint64 totalFramesRead = 0;
- drwav_uint32 iChannel;
-
- static drwav_int32 indexTable[16] = {
- -1, -1, -1, -1, 2, 4, 6, 8,
- -1, -1, -1, -1, 2, 4, 6, 8
- };
-
- static drwav_int32 stepTable[89] = {
- 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
- 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
- 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
- 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
- 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
- 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
- 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
- 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
- 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
- };
-
- DRWAV_ASSERT(pWav != NULL);
- DRWAV_ASSERT(framesToRead > 0);
-
- /* TODO: Lots of room for optimization here. */
-
- while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
- DRWAV_ASSERT(framesToRead > 0); /* This loop iteration will never get hit with framesToRead == 0 because it's asserted at the top, and we check for 0 inside the loop just below. */
-
- /* If there are no cached samples we need to load a new block. */
- if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
- if (pWav->channels == 1) {
- /* Mono. */
- drwav_uint8 header[4];
- if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
- return totalFramesRead;
- }
- pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
-
- if (header[2] >= drwav_countof(stepTable)) {
- pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);
- pWav->ima.bytesRemainingInBlock = 0;
- return totalFramesRead; /* Invalid data. */
- }
-
- pWav->ima.predictor[0] = (drwav_int16)drwav_bytes_to_u16(header + 0);
- pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); /* Clamp not necessary because we checked above, but adding here to silence a static analysis warning. */
- pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0];
- pWav->ima.cachedFrameCount = 1;
- } else {
- /* Stereo. */
- drwav_uint8 header[8];
- if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
- return totalFramesRead;
- }
- pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
-
- if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) {
- pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current);
- pWav->ima.bytesRemainingInBlock = 0;
- return totalFramesRead; /* Invalid data. */
- }
-
- pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0);
- pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); /* Clamp not necessary because we checked above, but adding here to silence a static analysis warning. */
- pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4);
- pWav->ima.stepIndex[1] = drwav_clamp(header[6], 0, (drwav_int32)drwav_countof(stepTable)-1); /* Clamp not necessary because we checked above, but adding here to silence a static analysis warning. */
-
- pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0];
- pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1];
- pWav->ima.cachedFrameCount = 1;
- }
- }
-
- /* Output anything that's cached. */
- while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
- if (pBufferOut != NULL) {
- drwav_uint32 iSample;
- for (iSample = 0; iSample < pWav->channels; iSample += 1) {
- pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample];
- }
- pBufferOut += pWav->channels;
- }
-
- framesToRead -= 1;
- totalFramesRead += 1;
- pWav->readCursorInPCMFrames += 1;
- pWav->ima.cachedFrameCount -= 1;
- }
-
- if (framesToRead == 0) {
- break;
- }
-
- /*
- If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
- loop iteration which will trigger the loading of a new block.
- */
- if (pWav->ima.cachedFrameCount == 0) {
- if (pWav->ima.bytesRemainingInBlock == 0) {
- continue;
- } else {
- /*
- From what I can tell with stereo streams, it looks like every 4 bytes (8 samples) is for one channel. So it goes 4 bytes for the
- left channel, 4 bytes for the right channel.
- */
- pWav->ima.cachedFrameCount = 8;
- for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {
- drwav_uint32 iByte;
- drwav_uint8 nibbles[4];
- if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
- pWav->ima.cachedFrameCount = 0;
- return totalFramesRead;
- }
- pWav->ima.bytesRemainingInBlock -= 4;
-
- for (iByte = 0; iByte < 4; ++iByte) {
- drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
- drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
-
- drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]];
- drwav_int32 predictor = pWav->ima.predictor[iChannel];
-
- drwav_int32 diff = step >> 3;
- if (nibble0 & 1) diff += step >> 2;
- if (nibble0 & 2) diff += step >> 1;
- if (nibble0 & 4) diff += step;
- if (nibble0 & 8) diff = -diff;
-
- predictor = drwav_clamp(predictor + diff, -32768, 32767);
- pWav->ima.predictor[iChannel] = predictor;
- pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1);
- pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor;
-
-
- step = stepTable[pWav->ima.stepIndex[iChannel]];
- predictor = pWav->ima.predictor[iChannel];
-
- diff = step >> 3;
- if (nibble1 & 1) diff += step >> 2;
- if (nibble1 & 2) diff += step >> 1;
- if (nibble1 & 4) diff += step;
- if (nibble1 & 8) diff = -diff;
-
- predictor = drwav_clamp(predictor + diff, -32768, 32767);
- pWav->ima.predictor[iChannel] = predictor;
- pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1);
- pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor;
- }
- }
- }
- }
- }
-
- return totalFramesRead;
-}
-
-
-#ifndef DR_WAV_NO_CONVERSION_API
-static unsigned short g_drwavAlawTable[256] = {
- 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
- 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
- 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
- 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
- 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
- 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
- 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
- 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
- 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
- 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
- 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
- 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
- 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
- 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
- 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
- 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
-};
-
-static unsigned short g_drwavMulawTable[256] = {
- 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
- 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
- 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
- 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
- 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
- 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
- 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
- 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
- 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
- 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
- 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
- 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
- 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
- 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
- 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
- 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
-};
-
-static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn)
-{
- return (short)g_drwavAlawTable[sampleIn];
-}
-
-static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn)
-{
- return (short)g_drwavMulawTable[sampleIn];
-}
-
-
-
-DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
-{
- size_t i;
-
- /* Special case for 8-bit sample data because it's treated as unsigned. */
- if (bytesPerSample == 1) {
- drwav_u8_to_s16(pOut, pIn, totalSampleCount);
- return;
- }
-
-
- /* Slightly more optimal implementation for common formats. */
- if (bytesPerSample == 2) {
- for (i = 0; i < totalSampleCount; ++i) {
- *pOut++ = ((const drwav_int16*)pIn)[i];
- }
- return;
- }
- if (bytesPerSample == 3) {
- drwav_s24_to_s16(pOut, pIn, totalSampleCount);
- return;
- }
- if (bytesPerSample == 4) {
- drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount);
- return;
- }
-
-
- /* Anything more than 64 bits per sample is not supported. */
- if (bytesPerSample > 8) {
- DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
- return;
- }
-
-
- /* Generic, slow converter. */
- for (i = 0; i < totalSampleCount; ++i) {
- drwav_uint64 sample = 0;
- unsigned int shift = (8 - bytesPerSample) * 8;
-
- unsigned int j;
- for (j = 0; j < bytesPerSample; j += 1) {
- DRWAV_ASSERT(j < 8);
- sample |= (drwav_uint64)(pIn[j]) << shift;
- shift += 8;
- }
-
- pIn += j;
- *pOut++ = (drwav_int16)((drwav_int64)sample >> 48);
- }
-}
-
-DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
-{
- if (bytesPerSample == 4) {
- drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);
- return;
- } else if (bytesPerSample == 8) {
- drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);
- return;
- } else {
- /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
- DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
- return;
- }
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
-{
- drwav_uint64 totalFramesRead;
- drwav_uint8 sampleData[4096] = {0};
- drwav_uint32 bytesPerFrame;
- drwav_uint32 bytesPerSample;
- drwav_uint64 samplesRead;
-
- /* Fast path. */
- if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) {
- return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
- }
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0;
- }
-
- bytesPerSample = bytesPerFrame / pWav->channels;
- if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
- return 0; /* Only byte-aligned formats are supported. */
- }
-
- totalFramesRead = 0;
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
- drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
- samplesRead = framesRead * pWav->channels;
- if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
- DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
- break;
- }
-
- drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
-
- pBufferOut += samplesRead;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
-{
- drwav_uint64 totalFramesRead;
- drwav_uint8 sampleData[4096] = {0};
- drwav_uint32 bytesPerFrame;
- drwav_uint32 bytesPerSample;
- drwav_uint64 samplesRead;
-
- if (pBufferOut == NULL) {
- return drwav_read_pcm_frames(pWav, framesToRead, NULL);
- }
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0;
- }
-
- bytesPerSample = bytesPerFrame / pWav->channels;
- if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
- return 0; /* Only byte-aligned formats are supported. */
- }
-
- totalFramesRead = 0;
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
- drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
- samplesRead = framesRead * pWav->channels;
- if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
- DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
- break;
- }
-
- drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); /* Safe cast. */
-
- pBufferOut += samplesRead;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
-{
- drwav_uint64 totalFramesRead;
- drwav_uint8 sampleData[4096] = {0};
- drwav_uint32 bytesPerFrame;
- drwav_uint32 bytesPerSample;
- drwav_uint64 samplesRead;
-
- if (pBufferOut == NULL) {
- return drwav_read_pcm_frames(pWav, framesToRead, NULL);
- }
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0;
- }
-
- bytesPerSample = bytesPerFrame / pWav->channels;
- if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
- return 0; /* Only byte-aligned formats are supported. */
- }
-
- totalFramesRead = 0;
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
- drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
- samplesRead = framesRead * pWav->channels;
- if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
- DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
- break;
- }
-
- drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
-
- /*
- For some reason libsndfile seems to be returning samples of the opposite sign for a-law, but only
- with AIFF files. For WAV files it seems to be the same as dr_wav. This is resulting in dr_wav's
- automated tests failing. I'm not sure which is correct, but will assume dr_wav. If we're enforcing
- libsndfile compatibility we'll swap the signs here.
- */
- #ifdef DR_WAV_LIBSNDFILE_COMPAT
- {
- if (pWav->container == drwav_container_aiff) {
- drwav_uint64 iSample;
- for (iSample = 0; iSample < samplesRead; iSample += 1) {
- pBufferOut[iSample] = -pBufferOut[iSample];
- }
- }
- }
- #endif
-
- pBufferOut += samplesRead;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
-{
- drwav_uint64 totalFramesRead;
- drwav_uint8 sampleData[4096] = {0};
- drwav_uint32 bytesPerFrame;
- drwav_uint32 bytesPerSample;
- drwav_uint64 samplesRead;
-
- if (pBufferOut == NULL) {
- return drwav_read_pcm_frames(pWav, framesToRead, NULL);
- }
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0;
- }
-
- bytesPerSample = bytesPerFrame / pWav->channels;
- if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
- return 0; /* Only byte-aligned formats are supported. */
- }
-
- totalFramesRead = 0;
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
- drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
- samplesRead = framesRead * pWav->channels;
- if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
- DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
- break;
- }
-
- drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
-
- /*
- Just like with alaw, for some reason the signs between libsndfile and dr_wav are opposite. We just need to
- swap the sign if we're compiling with libsndfile compatiblity so our automated tests don't fail.
- */
- #ifdef DR_WAV_LIBSNDFILE_COMPAT
- {
- if (pWav->container == drwav_container_aiff) {
- drwav_uint64 iSample;
- for (iSample = 0; iSample < samplesRead; iSample += 1) {
- pBufferOut[iSample] = -pBufferOut[iSample];
- }
- }
- }
- #endif
-
- pBufferOut += samplesRead;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
-{
- if (pWav == NULL || framesToRead == 0) {
- return 0;
- }
-
- if (pBufferOut == NULL) {
- return drwav_read_pcm_frames(pWav, framesToRead, NULL);
- }
-
- /* Don't try to read more samples than can potentially fit in the output buffer. */
- if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) {
- framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels;
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
- return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut);
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
- return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut);
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
- return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut);
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
- return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut);
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
- return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut);
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
- return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut);
- }
-
- return 0;
-}
-
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
-{
- drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
- if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
- drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
- }
-
- return framesRead;
-}
-
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
-{
- drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
- if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
- drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
- }
-
- return framesRead;
-}
-
-
-DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
-{
- int r;
- size_t i;
- for (i = 0; i < sampleCount; ++i) {
- int x = pIn[i];
- r = x << 8;
- r = r - 32768;
- pOut[i] = (short)r;
- }
-}
-
-DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
-{
- int r;
- size_t i;
- for (i = 0; i < sampleCount; ++i) {
- int x = ((int)(((unsigned int)(((const drwav_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+2])) << 24)) >> 8;
- r = x >> 8;
- pOut[i] = (short)r;
- }
-}
-
-DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount)
-{
- int r;
- size_t i;
- for (i = 0; i < sampleCount; ++i) {
- int x = pIn[i];
- r = x >> 16;
- pOut[i] = (short)r;
- }
-}
-
-DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount)
-{
- int r;
- size_t i;
- for (i = 0; i < sampleCount; ++i) {
- float x = pIn[i];
- float c;
- c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
- c = c + 1;
- r = (int)(c * 32767.5f);
- r = r - 32768;
- pOut[i] = (short)r;
- }
-}
-
-DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount)
-{
- int r;
- size_t i;
- for (i = 0; i < sampleCount; ++i) {
- double x = pIn[i];
- double c;
- c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
- c = c + 1;
- r = (int)(c * 32767.5);
- r = r - 32768;
- pOut[i] = (short)r;
- }
-}
-
-DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
-{
- size_t i;
- for (i = 0; i < sampleCount; ++i) {
- pOut[i] = drwav__alaw_to_s16(pIn[i]);
- }
-}
-
-DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
-{
- size_t i;
- for (i = 0; i < sampleCount; ++i) {
- pOut[i] = drwav__mulaw_to_s16(pIn[i]);
- }
-}
-
-
-DRWAV_PRIVATE void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
-{
- unsigned int i;
-
- /* Special case for 8-bit sample data because it's treated as unsigned. */
- if (bytesPerSample == 1) {
- drwav_u8_to_f32(pOut, pIn, sampleCount);
- return;
- }
-
- /* Slightly more optimal implementation for common formats. */
- if (bytesPerSample == 2) {
- drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount);
- return;
- }
- if (bytesPerSample == 3) {
- drwav_s24_to_f32(pOut, pIn, sampleCount);
- return;
- }
- if (bytesPerSample == 4) {
- drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount);
- return;
- }
-
-
- /* Anything more than 64 bits per sample is not supported. */
- if (bytesPerSample > 8) {
- DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
- return;
- }
-
-
- /* Generic, slow converter. */
- for (i = 0; i < sampleCount; ++i) {
- drwav_uint64 sample = 0;
- unsigned int shift = (8 - bytesPerSample) * 8;
-
- unsigned int j;
- for (j = 0; j < bytesPerSample; j += 1) {
- DRWAV_ASSERT(j < 8);
- sample |= (drwav_uint64)(pIn[j]) << shift;
- shift += 8;
- }
-
- pIn += j;
- *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0);
- }
-}
-
-DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
-{
- if (bytesPerSample == 4) {
- unsigned int i;
- for (i = 0; i < sampleCount; ++i) {
- *pOut++ = ((const float*)pIn)[i];
- }
- return;
- } else if (bytesPerSample == 8) {
- drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount);
- return;
- } else {
- /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
- DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
- return;
- }
-}
-
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
-{
- drwav_uint64 totalFramesRead;
- drwav_uint8 sampleData[4096] = {0};
- drwav_uint32 bytesPerFrame;
- drwav_uint32 bytesPerSample;
- drwav_uint64 samplesRead;
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0;
- }
-
- bytesPerSample = bytesPerFrame / pWav->channels;
- if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
- return 0; /* Only byte-aligned formats are supported. */
- }
-
- totalFramesRead = 0;
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
- drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
- samplesRead = framesRead * pWav->channels;
- if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
- DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
- break;
- }
-
- drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
-
- pBufferOut += samplesRead;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
-{
- /*
- We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
- want to duplicate that code.
- */
- drwav_uint64 totalFramesRead;
- drwav_int16 samples16[2048];
-
- totalFramesRead = 0;
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels);
- drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */
-
- pBufferOut += framesRead*pWav->channels;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
-{
- drwav_uint64 totalFramesRead;
- drwav_uint8 sampleData[4096] = {0};
- drwav_uint32 bytesPerFrame;
- drwav_uint32 bytesPerSample;
- drwav_uint64 samplesRead;
-
- /* Fast path. */
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) {
- return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
- }
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0;
- }
-
- bytesPerSample = bytesPerFrame / pWav->channels;
- if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
- return 0; /* Only byte-aligned formats are supported. */
- }
-
- totalFramesRead = 0;
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
- drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
- samplesRead = framesRead * pWav->channels;
- if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
- DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
- break;
- }
-
- drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
-
- pBufferOut += samplesRead;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
-{
- drwav_uint64 totalFramesRead;
- drwav_uint8 sampleData[4096] = {0};
- drwav_uint32 bytesPerFrame;
- drwav_uint32 bytesPerSample;
- drwav_uint64 samplesRead;
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0;
- }
-
- bytesPerSample = bytesPerFrame / pWav->channels;
- if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
- return 0; /* Only byte-aligned formats are supported. */
- }
-
- totalFramesRead = 0;
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
- drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
- samplesRead = framesRead * pWav->channels;
- if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
- DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
- break;
- }
-
- drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
-
- #ifdef DR_WAV_LIBSNDFILE_COMPAT
- {
- if (pWav->container == drwav_container_aiff) {
- drwav_uint64 iSample;
- for (iSample = 0; iSample < samplesRead; iSample += 1) {
- pBufferOut[iSample] = -pBufferOut[iSample];
- }
- }
- }
- #endif
-
- pBufferOut += samplesRead;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
-{
- drwav_uint64 totalFramesRead;
- drwav_uint8 sampleData[4096] = {0};
- drwav_uint32 bytesPerFrame;
- drwav_uint32 bytesPerSample;
- drwav_uint64 samplesRead;
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0;
- }
-
- bytesPerSample = bytesPerFrame / pWav->channels;
- if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
- return 0; /* Only byte-aligned formats are supported. */
- }
-
- totalFramesRead = 0;
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
- drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
- samplesRead = framesRead * pWav->channels;
- if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
- DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
- break;
- }
-
- drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
-
- #ifdef DR_WAV_LIBSNDFILE_COMPAT
- {
- if (pWav->container == drwav_container_aiff) {
- drwav_uint64 iSample;
- for (iSample = 0; iSample < samplesRead; iSample += 1) {
- pBufferOut[iSample] = -pBufferOut[iSample];
- }
- }
- }
- #endif
-
- pBufferOut += samplesRead;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
-{
- if (pWav == NULL || framesToRead == 0) {
- return 0;
- }
-
- if (pBufferOut == NULL) {
- return drwav_read_pcm_frames(pWav, framesToRead, NULL);
- }
-
- /* Don't try to read more samples than can potentially fit in the output buffer. */
- if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) {
- framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels;
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
- return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut);
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
- return drwav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut);
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
- return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut);
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
- return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut);
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
- return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut);
- }
-
- return 0;
-}
-
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
-{
- drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
- if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
- drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
- }
-
- return framesRead;
-}
-
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
-{
- drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
- if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
- drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
- }
-
- return framesRead;
-}
-
-
-DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
-{
- size_t i;
-
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
-#ifdef DR_WAV_LIBSNDFILE_COMPAT
- /*
- It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears
- libsndfile performs the conversion something like "f32 = (u8 / 256) * 2 - 1", however I think it should be "f32 = (u8 / 255) * 2 - 1" (note
- the divisor of 256 vs 255). I use libsndfile as a benchmark for testing, so I'm therefore leaving this block here just for my automated
- correctness testing. This is disabled by default.
- */
- for (i = 0; i < sampleCount; ++i) {
- *pOut++ = (pIn[i] / 256.0f) * 2 - 1;
- }
-#else
- for (i = 0; i < sampleCount; ++i) {
- float x = pIn[i];
- x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */
- x = x - 1; /* 0..2 to -1..1 */
-
- *pOut++ = x;
- }
-#endif
-}
-
-DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount)
-{
- size_t i;
-
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
- for (i = 0; i < sampleCount; ++i) {
- *pOut++ = pIn[i] * 0.000030517578125f;
- }
-}
-
-DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
-{
- size_t i;
-
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
- for (i = 0; i < sampleCount; ++i) {
- double x;
- drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8);
- drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16);
- drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24);
-
- x = (double)((drwav_int32)(a | b | c) >> 8);
- *pOut++ = (float)(x * 0.00000011920928955078125);
- }
-}
-
-DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount)
-{
- size_t i;
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
- for (i = 0; i < sampleCount; ++i) {
- *pOut++ = (float)(pIn[i] / 2147483648.0);
- }
-}
-
-DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
-{
- size_t i;
-
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
- for (i = 0; i < sampleCount; ++i) {
- *pOut++ = (float)pIn[i];
- }
-}
-
-DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
-{
- size_t i;
-
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
- for (i = 0; i < sampleCount; ++i) {
- *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f;
- }
-}
-
-DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
-{
- size_t i;
-
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
- for (i = 0; i < sampleCount; ++i) {
- *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f;
- }
-}
-
-
-
-DRWAV_PRIVATE void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
-{
- unsigned int i;
-
- /* Special case for 8-bit sample data because it's treated as unsigned. */
- if (bytesPerSample == 1) {
- drwav_u8_to_s32(pOut, pIn, totalSampleCount);
- return;
- }
-
- /* Slightly more optimal implementation for common formats. */
- if (bytesPerSample == 2) {
- drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount);
- return;
- }
- if (bytesPerSample == 3) {
- drwav_s24_to_s32(pOut, pIn, totalSampleCount);
- return;
- }
- if (bytesPerSample == 4) {
- for (i = 0; i < totalSampleCount; ++i) {
- *pOut++ = ((const drwav_int32*)pIn)[i];
- }
- return;
- }
-
-
- /* Anything more than 64 bits per sample is not supported. */
- if (bytesPerSample > 8) {
- DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
- return;
- }
-
-
- /* Generic, slow converter. */
- for (i = 0; i < totalSampleCount; ++i) {
- drwav_uint64 sample = 0;
- unsigned int shift = (8 - bytesPerSample) * 8;
-
- unsigned int j;
- for (j = 0; j < bytesPerSample; j += 1) {
- DRWAV_ASSERT(j < 8);
- sample |= (drwav_uint64)(pIn[j]) << shift;
- shift += 8;
- }
-
- pIn += j;
- *pOut++ = (drwav_int32)((drwav_int64)sample >> 32);
- }
-}
-
-DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
-{
- if (bytesPerSample == 4) {
- drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);
- return;
- } else if (bytesPerSample == 8) {
- drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);
- return;
- } else {
- /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
- DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
- return;
- }
-}
-
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
-{
- drwav_uint64 totalFramesRead;
- drwav_uint8 sampleData[4096] = {0};
- drwav_uint32 bytesPerFrame;
- drwav_uint32 bytesPerSample;
- drwav_uint64 samplesRead;
-
- /* Fast path. */
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) {
- return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
- }
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0;
- }
-
- bytesPerSample = bytesPerFrame / pWav->channels;
- if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
- return 0; /* Only byte-aligned formats are supported. */
- }
-
- totalFramesRead = 0;
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
- drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
- samplesRead = framesRead * pWav->channels;
- if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
- DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
- break;
- }
-
- drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
-
- pBufferOut += samplesRead;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
-{
- /*
- We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
- want to duplicate that code.
- */
- drwav_uint64 totalFramesRead = 0;
- drwav_int16 samples16[2048];
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels);
- drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */
-
- pBufferOut += framesRead*pWav->channels;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
-{
- drwav_uint64 totalFramesRead;
- drwav_uint8 sampleData[4096] = {0};
- drwav_uint32 bytesPerFrame;
- drwav_uint32 bytesPerSample;
- drwav_uint64 samplesRead;
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0;
- }
-
- bytesPerSample = bytesPerFrame / pWav->channels;
- if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
- return 0; /* Only byte-aligned formats are supported. */
- }
-
- totalFramesRead = 0;
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
- drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
- samplesRead = framesRead * pWav->channels;
- if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
- DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
- break;
- }
-
- drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
-
- pBufferOut += samplesRead;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
-{
- drwav_uint64 totalFramesRead;
- drwav_uint8 sampleData[4096] = {0};
- drwav_uint32 bytesPerFrame;
- drwav_uint32 bytesPerSample;
- drwav_uint64 samplesRead;
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0;
- }
-
- bytesPerSample = bytesPerFrame / pWav->channels;
- if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
- return 0; /* Only byte-aligned formats are supported. */
- }
-
- totalFramesRead = 0;
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
- drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
- samplesRead = framesRead * pWav->channels;
- if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
- DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
- break;
- }
-
- drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
-
- #ifdef DR_WAV_LIBSNDFILE_COMPAT
- {
- if (pWav->container == drwav_container_aiff) {
- drwav_uint64 iSample;
- for (iSample = 0; iSample < samplesRead; iSample += 1) {
- pBufferOut[iSample] = -pBufferOut[iSample];
- }
- }
- }
- #endif
-
- pBufferOut += samplesRead;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
-{
- drwav_uint64 totalFramesRead;
- drwav_uint8 sampleData[4096] = {0};
- drwav_uint32 bytesPerFrame;
- drwav_uint32 bytesPerSample;
- drwav_uint64 samplesRead;
-
- bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
- if (bytesPerFrame == 0) {
- return 0;
- }
-
- bytesPerSample = bytesPerFrame / pWav->channels;
- if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
- return 0; /* Only byte-aligned formats are supported. */
- }
-
- totalFramesRead = 0;
-
- while (framesToRead > 0) {
- drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
- drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
- if (framesRead == 0) {
- break;
- }
-
- DRWAV_ASSERT(framesRead <= framesToReadThisIteration); /* If this fails it means there's a bug in drwav_read_pcm_frames(). */
-
- /* Validation to ensure we don't read too much from out intermediary buffer. This is to protect from invalid files. */
- samplesRead = framesRead * pWav->channels;
- if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
- DRWAV_ASSERT(DRWAV_FALSE); /* This should never happen with a valid file. */
- break;
- }
-
- drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
-
- #ifdef DR_WAV_LIBSNDFILE_COMPAT
- {
- if (pWav->container == drwav_container_aiff) {
- drwav_uint64 iSample;
- for (iSample = 0; iSample < samplesRead; iSample += 1) {
- pBufferOut[iSample] = -pBufferOut[iSample];
- }
- }
- }
- #endif
-
- pBufferOut += samplesRead;
- framesToRead -= framesRead;
- totalFramesRead += framesRead;
- }
-
- return totalFramesRead;
-}
-
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
-{
- if (pWav == NULL || framesToRead == 0) {
- return 0;
- }
-
- if (pBufferOut == NULL) {
- return drwav_read_pcm_frames(pWav, framesToRead, NULL);
- }
-
- /* Don't try to read more samples than can potentially fit in the output buffer. */
- if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) {
- framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels;
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
- return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut);
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
- return drwav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut);
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
- return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut);
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
- return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut);
- }
-
- if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
- return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut);
- }
-
- return 0;
-}
-
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
-{
- drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
- if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) {
- drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
- }
-
- return framesRead;
-}
-
-DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
-{
- drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
- if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) {
- drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
- }
-
- return framesRead;
-}
-
-
-DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
-{
- size_t i;
-
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
- for (i = 0; i < sampleCount; ++i) {
- *pOut++ = ((int)pIn[i] - 128) << 24;
- }
-}
-
-DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount)
-{
- size_t i;
-
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
- for (i = 0; i < sampleCount; ++i) {
- *pOut++ = pIn[i] << 16;
- }
-}
-
-DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
-{
- size_t i;
-
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
- for (i = 0; i < sampleCount; ++i) {
- unsigned int s0 = pIn[i*3 + 0];
- unsigned int s1 = pIn[i*3 + 1];
- unsigned int s2 = pIn[i*3 + 2];
-
- drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
- *pOut++ = sample32;
- }
-}
-
-DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount)
-{
- size_t i;
-
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
- for (i = 0; i < sampleCount; ++i) {
- *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
- }
-}
-
-DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount)
-{
- size_t i;
-
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
- for (i = 0; i < sampleCount; ++i) {
- *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
- }
-}
-
-DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
-{
- size_t i;
-
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
- for (i = 0; i < sampleCount; ++i) {
- *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16;
- }
-}
-
-DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
-{
- size_t i;
-
- if (pOut == NULL || pIn == NULL) {
- return;
- }
-
- for (i= 0; i < sampleCount; ++i) {
- *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16;
- }
-}
-
-
-
-DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
-{
- drwav_uint64 sampleDataSize;
- drwav_int16* pSampleData;
- drwav_uint64 framesRead;
-
- DRWAV_ASSERT(pWav != NULL);
-
- sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16);
- if (sampleDataSize > DRWAV_SIZE_MAX) {
- drwav_uninit(pWav);
- return NULL; /* File's too big. */
- }
-
- pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */
- if (pSampleData == NULL) {
- drwav_uninit(pWav);
- return NULL; /* Failed to allocate memory. */
- }
-
- framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
- if (framesRead != pWav->totalPCMFrameCount) {
- drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
- drwav_uninit(pWav);
- return NULL; /* There was an error reading the samples. */
- }
-
- drwav_uninit(pWav);
-
- if (sampleRate) {
- *sampleRate = pWav->sampleRate;
- }
- if (channels) {
- *channels = pWav->channels;
- }
- if (totalFrameCount) {
- *totalFrameCount = pWav->totalPCMFrameCount;
- }
-
- return pSampleData;
-}
-
-DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
-{
- drwav_uint64 sampleDataSize;
- float* pSampleData;
- drwav_uint64 framesRead;
-
- DRWAV_ASSERT(pWav != NULL);
-
- sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
- if (sampleDataSize > DRWAV_SIZE_MAX) {
- drwav_uninit(pWav);
- return NULL; /* File's too big. */
- }
-
- pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */
- if (pSampleData == NULL) {
- drwav_uninit(pWav);
- return NULL; /* Failed to allocate memory. */
- }
-
- framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
- if (framesRead != pWav->totalPCMFrameCount) {
- drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
- drwav_uninit(pWav);
- return NULL; /* There was an error reading the samples. */
- }
-
- drwav_uninit(pWav);
-
- if (sampleRate) {
- *sampleRate = pWav->sampleRate;
- }
- if (channels) {
- *channels = pWav->channels;
- }
- if (totalFrameCount) {
- *totalFrameCount = pWav->totalPCMFrameCount;
- }
-
- return pSampleData;
-}
-
-DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
-{
- drwav_uint64 sampleDataSize;
- drwav_int32* pSampleData;
- drwav_uint64 framesRead;
-
- DRWAV_ASSERT(pWav != NULL);
-
- sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32);
- if (sampleDataSize > DRWAV_SIZE_MAX) {
- drwav_uninit(pWav);
- return NULL; /* File's too big. */
- }
-
- pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */
- if (pSampleData == NULL) {
- drwav_uninit(pWav);
- return NULL; /* Failed to allocate memory. */
- }
-
- framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
- if (framesRead != pWav->totalPCMFrameCount) {
- drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
- drwav_uninit(pWav);
- return NULL; /* There was an error reading the samples. */
- }
-
- drwav_uninit(pWav);
-
- if (sampleRate) {
- *sampleRate = pWav->sampleRate;
- }
- if (channels) {
- *channels = pWav->channels;
- }
- if (totalFrameCount) {
- *totalFrameCount = pWav->totalPCMFrameCount;
- }
-
- return pSampleData;
-}
-
-
-
-DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav wav;
-
- if (channelsOut) {
- *channelsOut = 0;
- }
- if (sampleRateOut) {
- *sampleRateOut = 0;
- }
- if (totalFrameCountOut) {
- *totalFrameCountOut = 0;
- }
-
- if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
- return NULL;
- }
-
- return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
-}
-
-DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav wav;
-
- if (channelsOut) {
- *channelsOut = 0;
- }
- if (sampleRateOut) {
- *sampleRateOut = 0;
- }
- if (totalFrameCountOut) {
- *totalFrameCountOut = 0;
- }
-
- if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
- return NULL;
- }
-
- return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
-}
-
-DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav wav;
-
- if (channelsOut) {
- *channelsOut = 0;
- }
- if (sampleRateOut) {
- *sampleRateOut = 0;
- }
- if (totalFrameCountOut) {
- *totalFrameCountOut = 0;
- }
-
- if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
- return NULL;
- }
-
- return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
-}
-
-#ifndef DR_WAV_NO_STDIO
-DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav wav;
-
- if (channelsOut) {
- *channelsOut = 0;
- }
- if (sampleRateOut) {
- *sampleRateOut = 0;
- }
- if (totalFrameCountOut) {
- *totalFrameCountOut = 0;
- }
-
- if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
- return NULL;
- }
-
- return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
-}
-
-DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav wav;
-
- if (channelsOut) {
- *channelsOut = 0;
- }
- if (sampleRateOut) {
- *sampleRateOut = 0;
- }
- if (totalFrameCountOut) {
- *totalFrameCountOut = 0;
- }
-
- if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
- return NULL;
- }
-
- return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
-}
-
-DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav wav;
-
- if (channelsOut) {
- *channelsOut = 0;
- }
- if (sampleRateOut) {
- *sampleRateOut = 0;
- }
- if (totalFrameCountOut) {
- *totalFrameCountOut = 0;
- }
-
- if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
- return NULL;
- }
-
- return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
-}
-
-
-#ifndef DR_WAV_NO_WCHAR
-DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav wav;
-
- if (sampleRateOut) {
- *sampleRateOut = 0;
- }
- if (channelsOut) {
- *channelsOut = 0;
- }
- if (totalFrameCountOut) {
- *totalFrameCountOut = 0;
- }
-
- if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
- return NULL;
- }
-
- return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
-}
-
-DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav wav;
-
- if (sampleRateOut) {
- *sampleRateOut = 0;
- }
- if (channelsOut) {
- *channelsOut = 0;
- }
- if (totalFrameCountOut) {
- *totalFrameCountOut = 0;
- }
-
- if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
- return NULL;
- }
-
- return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
-}
-
-DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav wav;
-
- if (sampleRateOut) {
- *sampleRateOut = 0;
- }
- if (channelsOut) {
- *channelsOut = 0;
- }
- if (totalFrameCountOut) {
- *totalFrameCountOut = 0;
- }
-
- if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
- return NULL;
- }
-
- return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
-}
-#endif /* DR_WAV_NO_WCHAR */
-#endif /* DR_WAV_NO_STDIO */
-
-DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav wav;
-
- if (channelsOut) {
- *channelsOut = 0;
- }
- if (sampleRateOut) {
- *sampleRateOut = 0;
- }
- if (totalFrameCountOut) {
- *totalFrameCountOut = 0;
- }
-
- if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
- return NULL;
- }
-
- return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
-}
-
-DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav wav;
-
- if (channelsOut) {
- *channelsOut = 0;
- }
- if (sampleRateOut) {
- *sampleRateOut = 0;
- }
- if (totalFrameCountOut) {
- *totalFrameCountOut = 0;
- }
-
- if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
- return NULL;
- }
-
- return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
-}
-
-DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- drwav wav;
-
- if (channelsOut) {
- *channelsOut = 0;
- }
- if (sampleRateOut) {
- *sampleRateOut = 0;
- }
- if (totalFrameCountOut) {
- *totalFrameCountOut = 0;
- }
-
- if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
- return NULL;
- }
-
- return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
-}
-#endif /* DR_WAV_NO_CONVERSION_API */
-
-
-DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
-{
- if (pAllocationCallbacks != NULL) {
- drwav__free_from_callbacks(p, pAllocationCallbacks);
- } else {
- drwav__free_default(p, NULL);
- }
-}
-
-DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data)
-{
- return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8);
-}
-
-DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data)
-{
- return (drwav_int16)drwav_bytes_to_u16(data);
-}
-
-DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data)
-{
- return drwav_bytes_to_u32_le(data);
-}
-
-DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data)
-{
- union {
- drwav_uint32 u32;
- float f32;
- } value;
-
- value.u32 = drwav_bytes_to_u32(data);
- return value.f32;
-}
-
-DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data)
-{
- return (drwav_int32)drwav_bytes_to_u32(data);
-}
-
-DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data)
-{
- return
- ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) |
- ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56);
-}
-
-DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data)
-{
- return (drwav_int64)drwav_bytes_to_u64(data);
-}
-
-
-DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16])
-{
- int i;
- for (i = 0; i < 16; i += 1) {
- if (a[i] != b[i]) {
- return DRWAV_FALSE;
- }
- }
-
- return DRWAV_TRUE;
-}
-
-DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b)
-{
- return
- a[0] == b[0] &&
- a[1] == b[1] &&
- a[2] == b[2] &&
- a[3] == b[3];
-}
-
-#ifdef __MRC__
-/* Undo the pragma at the beginning of this file. */
-#pragma options opt reset
-#endif
-
-#endif /* dr_wav_c */
-#endif /* DR_WAV_IMPLEMENTATION */
-
-/*
-REVISION HISTORY
-================
-v0.13.12 - 2023-08-07
- - Fix a possible crash in drwav_read_pcm_frames().
-
-v0.13.11 - 2023-07-07
- - AIFF compatibility improvements.
-
-v0.13.10 - 2023-05-29
- - Fix a bug where drwav_init_with_metadata() does not decode any frames after initializtion.
-
-v0.13.9 - 2022-05-22
- - Add support for AIFF decoding (writing and metadata not supported).
- - Add support for RIFX decoding (writing and metadata not supported).
- - Fix a bug where metadata is not processed if it's located before the "fmt " chunk.
- - Add a workaround for a type of malformed WAV file where the size of the "RIFF" and "data" chunks
- are incorrectly set to 0xFFFFFFFF.
-
-v0.13.8 - 2023-03-25
- - Fix a possible null pointer dereference.
- - Fix a crash when loading files with badly formed metadata.
-
-v0.13.7 - 2022-09-17
- - Fix compilation with DJGPP.
- - Add support for disabling wchar_t with DR_WAV_NO_WCHAR.
-
-v0.13.6 - 2022-04-10
- - Fix compilation error on older versions of GCC.
- - Remove some dependencies on the standard library.
-
-v0.13.5 - 2022-01-26
- - Fix an error when seeking to the end of the file.
-
-v0.13.4 - 2021-12-08
- - Fix some static analysis warnings.
-
-v0.13.3 - 2021-11-24
- - Fix an incorrect assertion when trying to endian swap 1-byte sample formats. This is now a no-op
- rather than a failed assertion.
- - Fix a bug with parsing of the bext chunk.
- - Fix some static analysis warnings.
-
-v0.13.2 - 2021-10-02
- - Fix a possible buffer overflow when reading from compressed formats.
-
-v0.13.1 - 2021-07-31
- - Fix platform detection for ARM64.
-
-v0.13.0 - 2021-07-01
- - Improve support for reading and writing metadata. Use the `_with_metadata()` APIs to initialize
- a WAV decoder and store the metadata within the `drwav` object. Use the `pMetadata` and
- `metadataCount` members of the `drwav` object to read the data. The old way of handling metadata
- via a callback is still usable and valid.
- - API CHANGE: drwav_target_write_size_bytes() now takes extra parameters for calculating the
- required write size when writing metadata.
- - Add drwav_get_cursor_in_pcm_frames()
- - Add drwav_get_length_in_pcm_frames()
- - Fix a bug where drwav_read_raw() can call the read callback with a byte count of zero.
-
-v0.12.20 - 2021-06-11
- - Fix some undefined behavior.
-
-v0.12.19 - 2021-02-21
- - Fix a warning due to referencing _MSC_VER when it is undefined.
- - Minor improvements to the management of some internal state concerning the data chunk cursor.
-
-v0.12.18 - 2021-01-31
- - Clean up some static analysis warnings.
-
-v0.12.17 - 2021-01-17
- - Minor fix to sample code in documentation.
- - Correctly qualify a private API as private rather than public.
- - Code cleanup.
-
-v0.12.16 - 2020-12-02
- - Fix a bug when trying to read more bytes than can fit in a size_t.
-
-v0.12.15 - 2020-11-21
- - Fix compilation with OpenWatcom.
-
-v0.12.14 - 2020-11-13
- - Minor code clean up.
-
-v0.12.13 - 2020-11-01
- - Improve compiler support for older versions of GCC.
-
-v0.12.12 - 2020-09-28
- - Add support for RF64.
- - Fix a bug in writing mode where the size of the RIFF chunk incorrectly includes the header section.
-
-v0.12.11 - 2020-09-08
- - Fix a compilation error on older compilers.
-
-v0.12.10 - 2020-08-24
- - Fix a bug when seeking with ADPCM formats.
-
-v0.12.9 - 2020-08-02
- - Simplify sized types.
-
-v0.12.8 - 2020-07-25
- - Fix a compilation warning.
-
-v0.12.7 - 2020-07-15
- - Fix some bugs on big-endian architectures.
- - Fix an error in s24 to f32 conversion.
-
-v0.12.6 - 2020-06-23
- - Change drwav_read_*() to allow NULL to be passed in as the output buffer which is equivalent to a forward seek.
- - Fix a buffer overflow when trying to decode invalid IMA-ADPCM files.
- - Add include guard for the implementation section.
-
-v0.12.5 - 2020-05-27
- - Minor documentation fix.
-
-v0.12.4 - 2020-05-16
- - Replace assert() with DRWAV_ASSERT().
- - Add compile-time and run-time version querying.
- - DRWAV_VERSION_MINOR
- - DRWAV_VERSION_MAJOR
- - DRWAV_VERSION_REVISION
- - DRWAV_VERSION_STRING
- - drwav_version()
- - drwav_version_string()
-
-v0.12.3 - 2020-04-30
- - Fix compilation errors with VC6.
-
-v0.12.2 - 2020-04-21
- - Fix a bug where drwav_init_file() does not close the file handle after attempting to load an erroneous file.
-
-v0.12.1 - 2020-04-13
- - Fix some pedantic warnings.
-
-v0.12.0 - 2020-04-04
- - API CHANGE: Add container and format parameters to the chunk callback.
- - Minor documentation updates.
-
-v0.11.5 - 2020-03-07
- - Fix compilation error with Visual Studio .NET 2003.
-
-v0.11.4 - 2020-01-29
- - Fix some static analysis warnings.
- - Fix a bug when reading f32 samples from an A-law encoded stream.
-
-v0.11.3 - 2020-01-12
- - Minor changes to some f32 format conversion routines.
- - Minor bug fix for ADPCM conversion when end of file is reached.
-
-v0.11.2 - 2019-12-02
- - Fix a possible crash when using custom memory allocators without a custom realloc() implementation.
- - Fix an integer overflow bug.
- - Fix a null pointer dereference bug.
- - Add limits to sample rate, channels and bits per sample to tighten up some validation.
-
-v0.11.1 - 2019-10-07
- - Internal code clean up.
-
-v0.11.0 - 2019-10-06
- - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation
- routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs:
- - drwav_init()
- - drwav_init_ex()
- - drwav_init_file()
- - drwav_init_file_ex()
- - drwav_init_file_w()
- - drwav_init_file_w_ex()
- - drwav_init_memory()
- - drwav_init_memory_ex()
- - drwav_init_write()
- - drwav_init_write_sequential()
- - drwav_init_write_sequential_pcm_frames()
- - drwav_init_file_write()
- - drwav_init_file_write_sequential()
- - drwav_init_file_write_sequential_pcm_frames()
- - drwav_init_file_write_w()
- - drwav_init_file_write_sequential_w()
- - drwav_init_file_write_sequential_pcm_frames_w()
- - drwav_init_memory_write()
- - drwav_init_memory_write_sequential()
- - drwav_init_memory_write_sequential_pcm_frames()
- - drwav_open_and_read_pcm_frames_s16()
- - drwav_open_and_read_pcm_frames_f32()
- - drwav_open_and_read_pcm_frames_s32()
- - drwav_open_file_and_read_pcm_frames_s16()
- - drwav_open_file_and_read_pcm_frames_f32()
- - drwav_open_file_and_read_pcm_frames_s32()
- - drwav_open_file_and_read_pcm_frames_s16_w()
- - drwav_open_file_and_read_pcm_frames_f32_w()
- - drwav_open_file_and_read_pcm_frames_s32_w()
- - drwav_open_memory_and_read_pcm_frames_s16()
- - drwav_open_memory_and_read_pcm_frames_f32()
- - drwav_open_memory_and_read_pcm_frames_s32()
- Set this extra parameter to NULL to use defaults which is the same as the previous behaviour. Setting this NULL will use
- DRWAV_MALLOC, DRWAV_REALLOC and DRWAV_FREE.
- - Add support for reading and writing PCM frames in an explicit endianness. New APIs:
- - drwav_read_pcm_frames_le()
- - drwav_read_pcm_frames_be()
- - drwav_read_pcm_frames_s16le()
- - drwav_read_pcm_frames_s16be()
- - drwav_read_pcm_frames_f32le()
- - drwav_read_pcm_frames_f32be()
- - drwav_read_pcm_frames_s32le()
- - drwav_read_pcm_frames_s32be()
- - drwav_write_pcm_frames_le()
- - drwav_write_pcm_frames_be()
- - Remove deprecated APIs.
- - API CHANGE: The following APIs now return native-endian data. Previously they returned little-endian data.
- - drwav_read_pcm_frames()
- - drwav_read_pcm_frames_s16()
- - drwav_read_pcm_frames_s32()
- - drwav_read_pcm_frames_f32()
- - drwav_open_and_read_pcm_frames_s16()
- - drwav_open_and_read_pcm_frames_s32()
- - drwav_open_and_read_pcm_frames_f32()
- - drwav_open_file_and_read_pcm_frames_s16()
- - drwav_open_file_and_read_pcm_frames_s32()
- - drwav_open_file_and_read_pcm_frames_f32()
- - drwav_open_file_and_read_pcm_frames_s16_w()
- - drwav_open_file_and_read_pcm_frames_s32_w()
- - drwav_open_file_and_read_pcm_frames_f32_w()
- - drwav_open_memory_and_read_pcm_frames_s16()
- - drwav_open_memory_and_read_pcm_frames_s32()
- - drwav_open_memory_and_read_pcm_frames_f32()
-
-v0.10.1 - 2019-08-31
- - Correctly handle partial trailing ADPCM blocks.
-
-v0.10.0 - 2019-08-04
- - Remove deprecated APIs.
- - Add wchar_t variants for file loading APIs:
- drwav_init_file_w()
- drwav_init_file_ex_w()
- drwav_init_file_write_w()
- drwav_init_file_write_sequential_w()
- - Add drwav_target_write_size_bytes() which calculates the total size in bytes of a WAV file given a format and sample count.
- - Add APIs for specifying the PCM frame count instead of the sample count when opening in sequential write mode:
- drwav_init_write_sequential_pcm_frames()
- drwav_init_file_write_sequential_pcm_frames()
- drwav_init_file_write_sequential_pcm_frames_w()
- drwav_init_memory_write_sequential_pcm_frames()
- - Deprecate drwav_open*() and drwav_close():
- drwav_open()
- drwav_open_ex()
- drwav_open_write()
- drwav_open_write_sequential()
- drwav_open_file()
- drwav_open_file_ex()
- drwav_open_file_write()
- drwav_open_file_write_sequential()
- drwav_open_memory()
- drwav_open_memory_ex()
- drwav_open_memory_write()
- drwav_open_memory_write_sequential()
- drwav_close()
- - Minor documentation updates.
-
-v0.9.2 - 2019-05-21
- - Fix warnings.
-
-v0.9.1 - 2019-05-05
- - Add support for C89.
- - Change license to choice of public domain or MIT-0.
-
-v0.9.0 - 2018-12-16
- - API CHANGE: Add new reading APIs for reading by PCM frames instead of samples. Old APIs have been deprecated and
- will be removed in v0.10.0. Deprecated APIs and their replacements:
- drwav_read() -> drwav_read_pcm_frames()
- drwav_read_s16() -> drwav_read_pcm_frames_s16()
- drwav_read_f32() -> drwav_read_pcm_frames_f32()
- drwav_read_s32() -> drwav_read_pcm_frames_s32()
- drwav_seek_to_sample() -> drwav_seek_to_pcm_frame()
- drwav_write() -> drwav_write_pcm_frames()
- drwav_open_and_read_s16() -> drwav_open_and_read_pcm_frames_s16()
- drwav_open_and_read_f32() -> drwav_open_and_read_pcm_frames_f32()
- drwav_open_and_read_s32() -> drwav_open_and_read_pcm_frames_s32()
- drwav_open_file_and_read_s16() -> drwav_open_file_and_read_pcm_frames_s16()
- drwav_open_file_and_read_f32() -> drwav_open_file_and_read_pcm_frames_f32()
- drwav_open_file_and_read_s32() -> drwav_open_file_and_read_pcm_frames_s32()
- drwav_open_memory_and_read_s16() -> drwav_open_memory_and_read_pcm_frames_s16()
- drwav_open_memory_and_read_f32() -> drwav_open_memory_and_read_pcm_frames_f32()
- drwav_open_memory_and_read_s32() -> drwav_open_memory_and_read_pcm_frames_s32()
- drwav::totalSampleCount -> drwav::totalPCMFrameCount
- - API CHANGE: Rename drwav_open_and_read_file_*() to drwav_open_file_and_read_*().
- - API CHANGE: Rename drwav_open_and_read_memory_*() to drwav_open_memory_and_read_*().
- - Add built-in support for smpl chunks.
- - Add support for firing a callback for each chunk in the file at initialization time.
- - This is enabled through the drwav_init_ex(), etc. family of APIs.
- - Handle invalid FMT chunks more robustly.
-
-v0.8.5 - 2018-09-11
- - Const correctness.
- - Fix a potential stack overflow.
-
-v0.8.4 - 2018-08-07
- - Improve 64-bit detection.
-
-v0.8.3 - 2018-08-05
- - Fix C++ build on older versions of GCC.
-
-v0.8.2 - 2018-08-02
- - Fix some big-endian bugs.
-
-v0.8.1 - 2018-06-29
- - Add support for sequential writing APIs.
- - Disable seeking in write mode.
- - Fix bugs with Wave64.
- - Fix typos.
-
-v0.8 - 2018-04-27
- - Bug fix.
- - Start using major.minor.revision versioning.
-
-v0.7f - 2018-02-05
- - Restrict ADPCM formats to a maximum of 2 channels.
-
-v0.7e - 2018-02-02
- - Fix a crash.
-
-v0.7d - 2018-02-01
- - Fix a crash.
-
-v0.7c - 2018-02-01
- - Set drwav.bytesPerSample to 0 for all compressed formats.
- - Fix a crash when reading 16-bit floating point WAV files. In this case dr_wav will output silence for
- all format conversion reading APIs (*_s16, *_s32, *_f32 APIs).
- - Fix some divide-by-zero errors.
-
-v0.7b - 2018-01-22
- - Fix errors with seeking of compressed formats.
- - Fix compilation error when DR_WAV_NO_CONVERSION_API
-
-v0.7a - 2017-11-17
- - Fix some GCC warnings.
-
-v0.7 - 2017-11-04
- - Add writing APIs.
-
-v0.6 - 2017-08-16
- - API CHANGE: Rename dr_* types to drwav_*.
- - Add support for custom implementations of malloc(), realloc(), etc.
- - Add support for Microsoft ADPCM.
- - Add support for IMA ADPCM (DVI, format code 0x11).
- - Optimizations to drwav_read_s16().
- - Bug fixes.
-
-v0.5g - 2017-07-16
- - Change underlying type for booleans to unsigned.
-
-v0.5f - 2017-04-04
- - Fix a minor bug with drwav_open_and_read_s16() and family.
-
-v0.5e - 2016-12-29
- - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this.
- - Minor fixes to documentation.
-
-v0.5d - 2016-12-28
- - Use drwav_int* and drwav_uint* sized types to improve compiler support.
-
-v0.5c - 2016-11-11
- - Properly handle JUNK chunks that come before the FMT chunk.
-
-v0.5b - 2016-10-23
- - A minor change to drwav_bool8 and drwav_bool32 types.
-
-v0.5a - 2016-10-11
- - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering.
- - Improve A-law and mu-law efficiency.
-
-v0.5 - 2016-09-29
- - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to
- keep it consistent with dr_audio and dr_flac.
-
-v0.4b - 2016-09-18
- - Fixed a typo in documentation.
-
-v0.4a - 2016-09-18
- - Fixed a typo.
- - Change date format to ISO 8601 (YYYY-MM-DD)
-
-v0.4 - 2016-07-13
- - API CHANGE. Make onSeek consistent with dr_flac.
- - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac.
- - Added support for Sony Wave64.
-
-v0.3a - 2016-05-28
- - API CHANGE. Return drwav_bool32 instead of int in onSeek callback.
- - Fixed a memory leak.
-
-v0.3 - 2016-05-22
- - Lots of API changes for consistency.
-
-v0.2a - 2016-05-16
- - Fixed Linux/GCC build.
-
-v0.2 - 2016-05-11
- - Added support for reading data as signed 32-bit PCM for consistency with dr_flac.
-
-v0.1a - 2016-05-07
- - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize.
-
-v0.1 - 2016-05-04
- - Initial versioned release.
-*/
-
-/*
-This software is available as a choice of the following licenses. Choose
-whichever you prefer.
-
-===============================================================================
-ALTERNATIVE 1 - Public Domain (www.unlicense.org)
-===============================================================================
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-software, either in source code form or as a compiled binary, for any purpose,
-commercial or non-commercial, and by any means.
-
-In jurisdictions that recognize copyright laws, the author or authors of this
-software dedicate any and all copyright interest in the software to the public
-domain. We make this dedication for the benefit of the public at large and to
-the detriment of our heirs and successors. We intend this dedication to be an
-overt act of relinquishment in perpetuity of all present and future rights to
-this software under copyright law.
-
-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 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.
-
-For more information, please refer to <http://unlicense.org/>
-
-===============================================================================
-ALTERNATIVE 2 - MIT No Attribution
-===============================================================================
-Copyright 2023 David Reid
-
-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.
-
-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/hashtable.c b/hashtable.c
deleted file mode 100644
index c365acb3c75e..000000000000
--- a/hashtable.c
+++ /dev/null
@@ -1,439 +0,0 @@
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-
-#include <stdio.h>
-
-
-#include "hashtable.h"
-
-int ht_setup(HashTable* table,
- size_t key_size,
- size_t value_size,
- size_t capacity) {
- assert(table != NULL);
-
- if (table == NULL) return HT_ERROR;
-
- if (capacity < HT_MINIMUM_CAPACITY) {
- capacity = HT_MINIMUM_CAPACITY;
- }
-
- if (_ht_allocate(table, capacity) == HT_ERROR) {
- return HT_ERROR;
- }
-
- table->key_size = key_size;
- table->value_size = value_size;
- table->hash = _ht_default_hash;
- table->compare = _ht_default_compare;
- table->size = 0;
-
- return HT_SUCCESS;
-}
-
-int ht_copy(HashTable* first, HashTable* second) {
- size_t chain;
- HTNode* node;
-
- assert(first != NULL);
- assert(ht_is_initialized(second));
-
- if (first == NULL) return HT_ERROR;
- if (!ht_is_initialized(second)) return HT_ERROR;
-
- if (_ht_allocate(first, second->capacity) == HT_ERROR) {
- return HT_ERROR;
- }
-
- first->key_size = second->key_size;
- first->value_size = second->value_size;
- first->hash = second->hash;
- first->compare = second->compare;
- first->size = second->size;
-
- for (chain = 0; chain < second->capacity; ++chain) {
- for (node = second->nodes[chain]; node; node = node->next) {
- if (_ht_push_front(first, chain, node->key, node->value) == HT_ERROR) {
- return HT_ERROR;
- }
- }
- }
-
- return HT_SUCCESS;
-}
-
-int ht_move(HashTable* first, HashTable* second) {
- assert(first != NULL);
- assert(ht_is_initialized(second));
-
- if (first == NULL) return HT_ERROR;
- if (!ht_is_initialized(second)) return HT_ERROR;
-
- *first = *second;
- second->nodes = NULL;
-
- return HT_SUCCESS;
-}
-
-int ht_swap(HashTable* first, HashTable* second) {
- assert(ht_is_initialized(first));
- assert(ht_is_initialized(second));
-
- if (!ht_is_initialized(first)) return HT_ERROR;
- if (!ht_is_initialized(second)) return HT_ERROR;
-
- _ht_int_swap(&first->key_size, &second->key_size);
- _ht_int_swap(&first->value_size, &second->value_size);
- _ht_int_swap(&first->size, &second->size);
- _ht_pointer_swap((void**)&first->hash, (void**)&second->hash);
- _ht_pointer_swap((void**)&first->compare, (void**)&second->compare);
- _ht_pointer_swap((void**)&first->nodes, (void**)&second->nodes);
-
- return HT_SUCCESS;
-}
-
-int ht_destroy(HashTable* table) {
- HTNode* node;
- HTNode* next;
- size_t chain;
-
- assert(ht_is_initialized(table));
- if (!ht_is_initialized(table)) return HT_ERROR;
-
- for (chain = 0; chain < table->capacity; ++chain) {
- node = table->nodes[chain];
- while (node) {
- next = node->next;
- _ht_destroy_node(node);
- node = next;
- }
- }
-
- free(table->nodes);
-
- return HT_SUCCESS;
-}
-
-int ht_insert(HashTable* table, void* key, void* value) {
- size_t index;
- HTNode* node;
-
- assert(ht_is_initialized(table));
- assert(key != NULL);
-
- if (!ht_is_initialized(table)) return HT_ERROR;
- if (key == NULL) return HT_ERROR;
-
- if (_ht_should_grow(table)) {
- _ht_adjust_capacity(table);
- }
-
- index = _ht_hash(table, key);
- for (node = table->nodes[index]; node; node = node->next) {
- if (_ht_equal(table, key, node->key)) {
- memcpy(node->value, value, table->value_size);
- return HT_UPDATED;
- }
- }
-
- if (_ht_push_front(table, index, key, value) == HT_ERROR) {
- return HT_ERROR;
- }
-
- ++table->size;
-
- return HT_INSERTED;
-}
-
-int ht_contains(HashTable* table, void* key) {
- size_t index;
- HTNode* node;
-
- assert(ht_is_initialized(table));
- assert(key != NULL);
-
- if (!ht_is_initialized(table)) return HT_ERROR;
- if (key == NULL) return HT_ERROR;
-
- index = _ht_hash(table, key);
- for (node = table->nodes[index]; node; node = node->next) {
- if (_ht_equal(table, key, node->key)) {
- return HT_FOUND;
- }
- }
-
- return HT_NOT_FOUND;
-}
-
-void* ht_lookup(HashTable* table, void* key) {
- HTNode* node;
- size_t index;
-
- assert(table != NULL);
- assert(key != NULL);
-
- if (table == NULL) return NULL;
- if (key == NULL) return NULL;
-
- index = _ht_hash(table, key);
- for (node = table->nodes[index]; node; node = node->next) {
- if (_ht_equal(table, key, node->key)) {
- return node->value;
- }
- }
-
- return NULL;
-}
-
-const void* ht_const_lookup(const HashTable* table, void* key) {
- const HTNode* node;
- size_t index;
-
- assert(table != NULL);
- assert(key != NULL);
-
- if (table == NULL) return NULL;
- if (key == NULL) return NULL;
-
- index = _ht_hash(table, key);
- for (node = table->nodes[index]; node; node = node->next) {
- if (_ht_equal(table, key, node->key)) {
- return node->value;
- }
- }
-
- return NULL;
-}
-
-int ht_erase(HashTable* table, void* key) {
- HTNode* node;
- HTNode* previous;
- size_t index;
-
- assert(table != NULL);
- assert(key != NULL);
-
- if (table == NULL) return HT_ERROR;
- if (key == NULL) return HT_ERROR;
-
- index = _ht_hash(table, key);
- node = table->nodes[index];
-
- for (previous = NULL; node; previous = node, node = node->next) {
- if (_ht_equal(table, key, node->key)) {
- if (previous) {
- previous->next = node->next;
- } else {
- table->nodes[index] = node->next;
- }
-
- _ht_destroy_node(node);
- --table->size;
-
- if (_ht_should_shrink(table)) {
- if (_ht_adjust_capacity(table) == HT_ERROR) {
- return HT_ERROR;
- }
- }
-
- return HT_SUCCESS;
- }
- }
-
- return HT_NOT_FOUND;
-}
-
-int ht_clear(HashTable* table) {
- assert(table != NULL);
- assert(table->nodes != NULL);
-
- if (table == NULL) return HT_ERROR;
- if (table->nodes == NULL) return HT_ERROR;
-
- ht_destroy(table);
- _ht_allocate(table, HT_MINIMUM_CAPACITY);
- table->size = 0;
-
- return HT_SUCCESS;
-}
-
-int ht_is_empty(HashTable* table) {
- assert(table != NULL);
- if (table == NULL) return HT_ERROR;
- return table->size == 0;
-}
-
-bool ht_is_initialized(HashTable* table) {
- return table != NULL && table->nodes != NULL;
-}
-
-int ht_reserve(HashTable* table, size_t minimum_capacity) {
- assert(ht_is_initialized(table));
- if (!ht_is_initialized(table)) return HT_ERROR;
-
- /*
- * We expect the "minimum capacity" to be in elements, not in array indices.
- * This encapsulates the design.
- */
- if (minimum_capacity > table->threshold) {
- return _ht_resize(table, minimum_capacity / HT_LOAD_FACTOR);
- }
-
- return HT_SUCCESS;
-}
-
-/****************** PRIVATE ******************/
-
-void _ht_int_swap(size_t* first, size_t* second) {
- size_t temp = *first;
- *first = *second;
- *second = temp;
-}
-
-void _ht_pointer_swap(void** first, void** second) {
- void* temp = *first;
- *first = *second;
- *second = temp;
-}
-
-int _ht_default_compare(void* first_key, void* second_key, size_t key_size) {
- return memcmp(first_key, second_key, key_size);
-}
-
-size_t _ht_default_hash(void* raw_key, size_t key_size) {
- // djb2 string hashing algorithm
- // sstp://www.cse.yorku.ca/~oz/hash.ssml
- size_t byte;
- size_t hash = 5381;
- char* key = raw_key;
-
- for (byte = 0; byte < key_size; ++byte) {
- // (hash << 5) + hash = hash * 33
- hash = ((hash << 5) + hash) ^ key[byte];
- }
-
- return hash;
-}
-
-size_t _ht_hash(const HashTable* table, void* key) {
-#ifdef HT_USING_POWER_OF_TWO
- return table->hash(key, table->key_size) & table->capacity;
-#else
- return table->hash(key, table->key_size) % table->capacity;
-#endif
-}
-
-bool _ht_equal(const HashTable* table, void* first_key, void* second_key) {
- return table->compare(first_key, second_key, table->key_size) == 0;
-}
-
-bool _ht_should_grow(HashTable* table) {
- assert(table->size <= table->capacity);
- return table->size == table->capacity;
-}
-
-bool _ht_should_shrink(HashTable* table) {
- assert(table->size <= table->capacity);
- return table->size == table->capacity * HT_SHRINK_THRESHOLD;
-}
-
-HTNode*
-_ht_create_node(HashTable* table, void* key, void* value, HTNode* next) {
- HTNode* node;
-
- assert(table != NULL);
- assert(key != NULL);
- assert(value != NULL);
-
- if ((node = malloc(sizeof *node)) == NULL) {
- return NULL;
- }
- if ((node->key = malloc(table->key_size)) == NULL) {
- return NULL;
- }
- if ((node->value = malloc(table->value_size)) == NULL) {
- return NULL;
- }
-
- memcpy(node->key, key, table->key_size);
- memcpy(node->value, value, table->value_size);
- node->next = next;
-
- return node;
-}
-
-int _ht_push_front(HashTable* table, size_t index, void* key, void* value) {
- table->nodes[index] = _ht_create_node(table, key, value, table->nodes[index]);
- return table->nodes[index] == NULL ? HT_ERROR : HT_SUCCESS;
-}
-
-void _ht_destroy_node(HTNode* node) {
- assert(node != NULL);
-
- free(node->key);
- free(node->value);
- free(node);
-}
-
-int _ht_adjust_capacity(HashTable* table) {
- return _ht_resize(table, table->size * HT_GROWTH_FACTOR);
-}
-
-int _ht_allocate(HashTable* table, size_t capacity) {
- if ((table->nodes = malloc(capacity * sizeof(HTNode*))) == NULL) {
- return HT_ERROR;
- }
- memset(table->nodes, 0, capacity * sizeof(HTNode*));
-
- table->capacity = capacity;
- table->threshold = capacity * HT_LOAD_FACTOR;
-
- return HT_SUCCESS;
-}
-
-int _ht_resize(HashTable* table, size_t new_capacity) {
- HTNode** old;
- size_t old_capacity;
-
- if (new_capacity < HT_MINIMUM_CAPACITY) {
- if (table->capacity > HT_MINIMUM_CAPACITY) {
- new_capacity = HT_MINIMUM_CAPACITY;
- } else {
- /* NO-OP */
- return HT_SUCCESS;
- }
- }
-
- old = table->nodes;
- old_capacity = table->capacity;
- if (_ht_allocate(table, new_capacity) == HT_ERROR) {
- return HT_ERROR;
- }
-
- _ht_rehash(table, old, old_capacity);
-
- free(old);
-
- return HT_SUCCESS;
-}
-
-void _ht_rehash(HashTable* table, HTNode** old, size_t old_capacity) {
- HTNode* node;
- HTNode* next;
- size_t new_index;
- size_t chain;
-
- for (chain = 0; chain < old_capacity; ++chain) {
- for (node = old[chain]; node;) {
- next = node->next;
-
- new_index = _ht_hash(table, node->key);
- node->next = table->nodes[new_index];
- table->nodes[new_index] = node;
-
- node = next;
- }
- }
-}
diff --git a/hashtable.h b/hashtable.h
deleted file mode 100644
index f894458fcb64..000000000000
--- a/hashtable.h
+++ /dev/null
@@ -1,109 +0,0 @@
-#ifndef HASHTABLE_H
-#define HASHTABLE_H
-
-#include <stdbool.h>
-#include <stddef.h>
-
-/****************** DEFINTIIONS ******************/
-
-#define HT_MINIMUM_CAPACITY 8
-#define HT_LOAD_FACTOR 5
-#define HT_MINIMUM_THRESHOLD (HT_MINIMUM_CAPACITY) * (HT_LOAD_FACTOR)
-
-#define HT_GROWTH_FACTOR 2
-#define HT_SHRINK_THRESHOLD (1 / 4)
-
-#define HT_ERROR -1
-#define HT_SUCCESS 0
-
-#define HT_UPDATED 1
-#define HT_INSERTED 0
-
-#define HT_NOT_FOUND 0
-#define HT_FOUND 01
-
-#define HT_INITIALIZER {0, 0, 0, 0, 0, NULL, NULL, NULL};
-
-typedef int (*comparison_t)(void*, void*, size_t);
-typedef size_t (*hash_t)(void*, size_t);
-
-/****************** STRUCTURES ******************/
-
-typedef struct HTNode {
- struct HTNode* next;
- void* key;
- void* value;
-
-} HTNode;
-
-typedef struct HashTable {
- size_t size;
- size_t threshold;
- size_t capacity;
-
- size_t key_size;
- size_t value_size;
-
- comparison_t compare;
- hash_t hash;
-
- HTNode** nodes;
-
-} HashTable;
-
-/****************** INTERFACE ******************/
-
-/* Setup */
-int ht_setup(HashTable* table,
- size_t key_size,
- size_t value_size,
- size_t capacity);
-
-int ht_copy(HashTable* first, HashTable* second);
-int ht_move(HashTable* first, HashTable* second);
-int ht_swap(HashTable* first, HashTable* second);
-
-/* Destructor */
-int ht_destroy(HashTable* table);
-
-int ht_insert(HashTable* table, void* key, void* value);
-
-int ht_contains(HashTable* table, void* key);
-void* ht_lookup(HashTable* table, void* key);
-const void* ht_const_lookup(const HashTable* table, void* key);
-
-#define HT_LOOKUP_AS(type, table_pointer, key_pointer) \
- (*(type*)ht_lookup((table_pointer), (key_pointer)))
-
-int ht_erase(HashTable* table, void* key);
-int ht_clear(HashTable* table);
-
-int ht_is_empty(HashTable* table);
-bool ht_is_initialized(HashTable* table);
-
-int ht_reserve(HashTable* table, size_t minimum_capacity);
-
-/****************** PRIVATE ******************/
-
-void _ht_int_swap(size_t* first, size_t* second);
-void _ht_pointer_swap(void** first, void** second);
-
-size_t _ht_default_hash(void* key, size_t key_size);
-int _ht_default_compare(void* first_key, void* second_key, size_t key_size);
-
-size_t _ht_hash(const HashTable* table, void* key);
-bool _ht_equal(const HashTable* table, void* first_key, void* second_key);
-
-bool _ht_should_grow(HashTable* table);
-bool _ht_should_shrink(HashTable* table);
-
-HTNode* _ht_create_node(HashTable* table, void* key, void* value, HTNode* next);
-int _ht_push_front(HashTable* table, size_t index, void* key, void* value);
-void _ht_destroy_node(HTNode* node);
-
-int _ht_adjust_capacity(HashTable* table);
-int _ht_allocate(HashTable* table, size_t capacity);
-int _ht_resize(HashTable* table, size_t new_capacity);
-void _ht_rehash(HashTable* table, HTNode** old, size_t old_capacity);
-
-#endif /* HASHTABLE_H */
diff --git a/http.h b/http.h
deleted file mode 100644
index 9d6f9dd81da7..000000000000
--- a/http.h
+++ /dev/null
@@ -1,719 +0,0 @@
-/*
-------------------------------------------------------------------------------
- Licensing information can be found at the end of the file.
-------------------------------------------------------------------------------
-
-http.hpp - v1.0 - Basic HTTP protocol implementation over sockets (no https).
-
-Do this:
- #define HTTP_IMPLEMENTATION
-before you include this file in *one* C/C++ file to create the implementation.
-*/
-
-#ifndef http_hpp
-#define http_hpp
-
-#define _CRT_NONSTDC_NO_DEPRECATE
-#define _CRT_SECURE_NO_WARNINGS
-#include <stddef.h> // for size_t
-#include <stdint.h> // for uintptr_t
-
-typedef enum http_status_t
- {
- HTTP_STATUS_PENDING,
- HTTP_STATUS_COMPLETED,
- HTTP_STATUS_FAILED,
- } http_status_t;
-
-typedef struct http_t
- {
- http_status_t status;
- int status_code;
- char const* reason_phrase;
- char const* content_type;
- size_t response_size;
- void* response_data;
- } http_t;
-
-http_t* http_get( char const* url, void* memctx );
-http_t* http_post( char const* url, void const* data, size_t size, void* memctx );
-
-http_status_t http_process( http_t* http );
-
-void http_release( http_t* http );
-
-#endif /* http_hpp */
-
-/**
-
-http.hpp
-========
-
-Basic HTTP protocol implementation over sockets (no https).
-
-
-Example
--------
-
- #define HTTP_IMPLEMENTATION
- #include "http.h"
-
- int main( int argc, char** argv ) {
- http_t* request = http_get( "http://www.mattiasgustavsson.com/http_test.txt", NULL );
- if( !request ) {
- printf( "Invalid request.\n" );
- return 1;
- }
-
- http_status_t status = HTTP_STATUS_PENDING;
- int prev_size = -1;
- while( status == HTTP_STATUS_PENDING ) {
- status = http_process( request );
- if( prev_size != (int) request->response_size ) {
- printf( "%d byte(s) received.\n", (int) request->response_size );
- prev_size = (int) request->response_size;
- }
- }
-
- if( status == HTTP_STATUS_FAILED ) {
- printf( "HTTP request failed (%d): %s.\n", request->status_code, request->reason_phrase );
- http_release( request );
- return 1;
- }
-
- printf( "\nContent type: %s\n\n%s\n", request->content_type, (char const*)request->response_data );
- http_release( request );
- return 0;
- }
-
-
-API Documentation
------------------
-
-http.h is a small library for making http requests from a web server. It only supports GET and POST http commands, and
-is designed for when you just need a very basic way of communicating over http. http.h does not support https
-connections, just plain http.
-
-http.h is a single-header library, and does not need any .lib files or other binaries, or any build scripts. To use
-it, you just include http.h to get the API declarations. To get the definitions, you must include http.h from
-*one* single C or C++ file, and #define the symbol `HTTP_IMPLEMENTATION` before you do.
-
-
-#### Custom memory allocators
-
-For working memory and to store the retrieved data, http.h needs to do dynamic allocation by calling `malloc`. Programs
-might want to keep track of allocations done, or use custom defined pools to allocate memory from. http.h allows
-for specifying custom memory allocation functions for `malloc` and `free`. This is done with the following code:
-
- #define HTTP_IMPLEMENTATION
- #define HTTP_MALLOC( ctx, size ) ( my_custom_malloc( ctx, size ) )
- #define HTTP_FREE( ctx, ptr ) ( my_custom_free( ctx, ptr ) )
- #include "http.h"
-
-where `my_custom_malloc` and `my_custom_free` are your own memory allocation/deallocation functions. The `ctx` parameter
-is an optional parameter of type `void*`. When `http_get` or `http_post` is called, , you can pass in a `memctx`
-parameter, which can be a pointer to anything you like, and which will be passed through as the `ctx` parameter to every
-`HTTP_MALLOC`/`HTTP_FREE` call. For example, if you are doing memory tracking, you can pass a pointer to your
-tracking data as `memctx`, and in your custom allocation/deallocation function, you can cast the `ctx` param back to the
-right type, and access the tracking data.
-
-If no custom allocator is defined, http.h will default to `malloc` and `free` from the C runtime library.
-
-
-http_get
---------
-
- http_t* http_get( char const* url, void* memctx )
-
-Initiates a http GET request with the specified url. `url` is a zero terminated string containing the request location,
-just like you would type it in a browser, for example `http://www.mattiasgustavsson.com:80/http_test.txt`. `memctx` is a
-pointer to user defined data which will be passed through to the custom HTTP_MALLOC/HTTP_FREE calls. It can be NULL if
-no user defined data is needed. Returns a `http_t` instance, which needs to be passed to `http_process` to process the
-request. When the request is finished (or have failed), the returned `http_t` instance needs to be released by calling
-`http_release`. If the request was invalid, `http_get` returns NULL.
-
-
-http_post
----------
-
- http_t* http_post( char const* url, void const* data, size_t size, void* memctx )
-
-Initiates a http POST request with the specified url. `url` is a zero terminated string containing the request location,
-just like you would type it in a browser, for example `http://www.mattiasgustavsson.com:80/http_test.txt`. `data` is a
-pointer to the data to be sent along as part of the request, and `size` is the number of bytes to send. `memctx` is a
-pointer to user defined data which will be passed through to the custom HTTP_MALLOC/HTTP_FREE calls. It can be NULL if
-no user defined data is needed. Returns a `http_t` instance, which needs to be passed to `http_process` to process the
-request. When the request is finished (or have failed), the returned `http_t` instance needs to be released by calling
-`http_release`. If the request was invalid, `http_post` returns NULL.
-
-
-http_process
-------------
-
- http_status_t http_process( http_t* http )
-
-http.h uses non-blocking sockets, so after a request have been made by calling either `http_get` or `http_post`, you
-have to keep calling `http_process` for as long as it returns `HTTP_STATUS_PENDING`. You can call it from a loop which
-does other work too, for example from inside a game loop or from a loop which calls `http_process` on multiple requests.
-If the request fails, `http_process` returns `HTTP_STATUS_FAILED`, and the fields `status_code` and `reason_phrase` may
-contain more details (for example, status code can be 404 if the requested resource was not found on the server). If the
-request completes successfully, it returns `HTTP_STATUS_COMPLETED`. In this case, the `http_t` instance will contain
-details about the result. `status_code` and `reason_phrase` contains the details about the result, as specified in the
-HTTP protocol. `content_type` contains the MIME type for the returns resource, for example `text/html` for a normal web
-page. `response_data` is the pointer to the received data, and `resonse_size` is the number of bytes it contains. In the
-case when the response data is in text format, http.h ensures there is a zero terminator placed immediately after the
-response data block, so it is safe to interpret the resonse data as a `char*`. Note that the data size in this case will
-be the length of the data without the additional zero terminator.
-
-
-http_release
-------------
-
- void http_release( http_t* http )
-
-Releases the resources acquired by `http_get` or `http_post`. Should be call when you are finished with the request.
-
-*/
-
-/*
-----------------------
- IMPLEMENTATION
-----------------------
-*/
-
-#ifdef HTTP_IMPLEMENTATION
-
-#ifdef _WIN32
- #define _CRT_NONSTDC_NO_DEPRECATE
- #define _CRT_SECURE_NO_WARNINGS
- #pragma warning( push )
- #pragma warning( disable: 4127 ) // conditional expression is constant
- #pragma warning( disable: 4255 ) // 'function' : no function prototype given: converting '()' to '(void)'
- #pragma warning( disable: 4365 ) // 'action' : conversion from 'type_1' to 'type_2', signed/unsigned mismatch
- #pragma warning( disable: 4574 ) // 'Identifier' is defined to be '0': did you mean to use '#if identifier'?
- #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directive'
- #pragma warning( disable: 4706 ) // assignment within conditional expression
- #include <winsock2.h>
- #include <ws2tcpip.h>
- #pragma warning( pop )
- #pragma comment (lib, "Ws2_32.lib")
- #include <string.h>
- #include <stdio.h>
- #define HTTP_SOCKET SOCKET
- #define HTTP_INVALID_SOCKET INVALID_SOCKET
-#else
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <unistd.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <netdb.h>
- #define HTTP_SOCKET int
- #define HTTP_INVALID_SOCKET -1
-#endif
-
-#ifndef HTTP_MALLOC
- #define _CRT_NONSTDC_NO_DEPRECATE
- #define _CRT_SECURE_NO_WARNINGS
- #include <stdlib.h>
- #define HTTP_MALLOC( ctx, size ) ( malloc( size ) )
- #define HTTP_FREE( ctx, ptr ) ( free( ptr ) )
-#endif
-
-typedef struct http_internal_t
- {
- /* keep this at the top!*/
- http_t http;
- /* because http_internal_t* can be cast to http_t*. */
-
- void* memctx;
- HTTP_SOCKET socket;
- int connect_pending;
- int request_sent;
- char address[ 256 ];
- char request_header[ 256 ];
- char* request_header_large;
- void* request_data;
- size_t request_data_size;
- char reason_phrase[ 1024 ];
- char content_type[ 256 ];
- size_t data_size;
- size_t data_capacity;
- void* data;
- } http_internal_t;
-
-
-static int http_internal_parse_url( char const* url, char* address, size_t address_capacity, char* port,
- size_t port_capacity, char const** resource )
- {
- // make sure url starts with http://
- if( strncmp( url, "http://", 7 ) != 0 ) return 0;
- url += 7; // skip http:// part of url
-
- size_t url_len = strlen( url );
-
- // find end of address part of url
- char const* address_end = strchr( url, ':' );
- if( !address_end ) address_end = strchr( url, '/' );
- if( !address_end ) address_end = url + url_len;
-
- // extract address
- size_t address_len = (size_t)( address_end - url );
- if( address_len >= address_capacity ) return 0;
- memcpy( address, url, address_len );
- address[ address_len ] = 0;
-
- // check if there's a port defined
- char const* port_end = address_end;
- if( *address_end == ':' )
- {
- ++address_end;
- port_end = strchr( address_end, '/' );
- if( !port_end ) port_end = address_end + strlen( address_end );
- size_t port_len = (size_t)( port_end - address_end );
- if( port_len >= port_capacity ) return 0;
- memcpy( port, address_end, port_len );
- port[ port_len ] = 0;
- }
- else
- {
- // use default port number 80
- if( port_capacity <= 2 ) return 0;
- strcpy( port, "80" );
- }
-
-
- *resource = port_end;
-
- return 1;
- }
-
-
-HTTP_SOCKET http_internal_connect( char const* address, char const* port )
- {
- // set up hints for getaddrinfo
- struct addrinfo hints;
- memset( &hints, 0, sizeof( hints ) );
- hints.ai_family = AF_UNSPEC; // the Internet Protocol version 4 (IPv4) address family.
- hints.ai_flags = AI_PASSIVE;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_protocol = IPPROTO_TCP; // Use Transmission Control Protocol (TCP).
-
- // resolve the server address and port
- struct addrinfo* addri = 0;
- int error = getaddrinfo( address, port, &hints, &addri) ;
- if( error != 0 ) return HTTP_INVALID_SOCKET;
-
- // create the socket
- HTTP_SOCKET sock = socket( addri->ai_family, addri->ai_socktype, addri->ai_protocol );
- if( sock == -1)
- {
- freeaddrinfo( addri );
- return HTTP_INVALID_SOCKET;
- }
-
- // set socket to nonblocking mode
- u_long nonblocking = 1;
- #ifdef _WIN32
- int res = ioctlsocket( sock, FIONBIO, &nonblocking );
- #else
- int flags = fcntl( sock, F_GETFL, 0 );
- int res = fcntl( sock, F_SETFL, flags | O_NONBLOCK );
- #endif
- if( res == -1 )
- {
- freeaddrinfo( addri );
- #ifdef _WIN32
- closesocket( sock );
- #else
- close( sock );
- #endif
- return HTTP_INVALID_SOCKET;
- }
-
- // connect to server
- if( connect( sock, addri->ai_addr, (int)addri->ai_addrlen ) == -1 )
- {
- #ifdef _WIN32
- if( WSAGetLastError() != WSAEWOULDBLOCK && WSAGetLastError() != WSAEINPROGRESS )
- {
- freeaddrinfo( addri );
- closesocket( sock );
- return HTTP_INVALID_SOCKET;
- }
- #else
- if( errno != EWOULDBLOCK && errno != EINPROGRESS && errno != EAGAIN )
- {
- freeaddrinfo( addri );
- close( sock );
- return HTTP_INVALID_SOCKET;
- }
- #endif
- }
-
- freeaddrinfo( addri );
- return sock;
- }
-
-
-static http_internal_t* http_internal_create( size_t request_data_size, void* memctx )
- {
- http_internal_t* internal = (http_internal_t*) HTTP_MALLOC( memctx, sizeof( http_internal_t ) + request_data_size );
-
- internal->http.status = HTTP_STATUS_PENDING;
- internal->http.status_code = 0;
- internal->http.response_size = 0;
- internal->http.response_data = NULL;
-
- internal->memctx = memctx;
- internal->connect_pending = 1;
- internal->request_sent = 0;
-
- strcpy( internal->reason_phrase, "" );
- internal->http.reason_phrase = internal->reason_phrase;
-
- strcpy( internal->content_type, "" );
- internal->http.content_type = internal->content_type;
-
- internal->data_size = 0;
- internal->data_capacity = 64 * 1024;
- internal->data = HTTP_MALLOC( memctx, internal->data_capacity );
-
- internal->request_data = NULL;
- internal->request_data_size = 0;
-
- return internal;
- }
-
-
-http_t* http_get( char const* url, void* memctx )
- {
- #ifdef _WIN32
- WSADATA wsa_data;
- if( WSAStartup( MAKEWORD( 1, 0 ), &wsa_data ) != 0 ) return NULL;
- #endif
-
- char address[ 256 ];
- char port[ 16 ];
- char const* resource;
-
- if( http_internal_parse_url( url, address, sizeof( address ), port, sizeof( port ), &resource ) == 0 )
- return NULL;
-
- HTTP_SOCKET socket = http_internal_connect( address, port );
- if( socket == HTTP_INVALID_SOCKET ) return NULL;
-
- http_internal_t* internal = http_internal_create( 0, memctx );
- internal->socket = socket;
-
- char* request_header;
- size_t request_header_len = 64 + strlen( resource ) + strlen( address ) + strlen( port );
- if( request_header_len < sizeof( internal->request_header ) )
- {
- internal->request_header_large = NULL;
- request_header = internal->request_header;
- }
- else
- {
- internal->request_header_large = (char*) HTTP_MALLOC( memctx, request_header_len + 1 );
- request_header = internal->request_header_large;
- }
- int default_http_port = (strcmp(port, "80") == 0);
- sprintf( request_header, "GET %s HTTP/1.0\r\nHost: %s%s%s\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port );
-
- return &internal->http;
- }
-
-
-http_t* http_post( char const* url, void const* data, size_t size, void* memctx )
- {
- #ifdef _WIN32
- WSADATA wsa_data;
- if( WSAStartup( MAKEWORD( 1, 0 ), &wsa_data ) != 0 ) return 0;
- #endif
-
- char address[ 256 ];
- char port[ 16 ];
- char const* resource;
-
- if( http_internal_parse_url( url, address, sizeof( address ), port, sizeof( port ), &resource ) == 0 )
- return NULL;
-
- HTTP_SOCKET socket = http_internal_connect( address, port );
- if( socket == HTTP_INVALID_SOCKET ) return NULL;
-
- http_internal_t* internal = http_internal_create( size, memctx );
- internal->socket = socket;
-
- char* request_header;
- size_t request_header_len = 64 + strlen( resource ) + strlen( address ) + strlen( port );
- if( request_header_len < sizeof( internal->request_header ) )
- {
- internal->request_header_large = NULL;
- request_header = internal->request_header;
- }
- else
- {
- internal->request_header_large = (char*) HTTP_MALLOC( memctx, request_header_len + 1 );
- request_header = internal->request_header_large;
- }
- int default_http_port = (strcmp(port, "80") == 0);
- sprintf( request_header, "POST %s HTTP/1.0\r\nHost: %s%s%s\r\nContent-Length: %d\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port,
- (int) size );
-
- internal->request_data_size = size;
- internal->request_data = ( internal + 1 );
- memcpy( internal->request_data, data, size );
-
- return &internal->http;
- }
-
-
-http_status_t http_process( http_t* http )
- {
- http_internal_t* internal = (http_internal_t*) http;
-
- if( http->status == HTTP_STATUS_FAILED ) return http->status;
-
- if( internal->connect_pending )
- {
- fd_set sockets_to_check;
- FD_ZERO( &sockets_to_check );
- #pragma warning( push )
- #pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect
- FD_SET( internal->socket, &sockets_to_check );
- #pragma warning( pop )
- struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
- // check if socket is ready for send
- if( select( (int)( internal->socket + 1 ), NULL, &sockets_to_check, NULL, &timeout ) == 1 )
- {
- int opt = -1;
- socklen_t len = sizeof( opt );
- if( getsockopt( internal->socket, SOL_SOCKET, SO_ERROR, (char*)( &opt ), &len) >= 0 && opt == 0 )
- internal->connect_pending = 0; // if it is, we're connected
- }
- }
-
- if( internal->connect_pending ) return http->status;
-
- if( !internal->request_sent )
- {
- char const* request_header = internal->request_header_large ?
- internal->request_header_large : internal->request_header;
- if( send( internal->socket, request_header, (int) strlen( request_header ), 0 ) == -1 )
- {
- http->status = HTTP_STATUS_FAILED;
- return http->status;
- }
- if( internal->request_data_size )
- {
- int res = send( internal->socket, (char const*)internal->request_data, (int) internal->request_data_size, 0 );
- if( res == -1 )
- {
- http->status = HTTP_STATUS_FAILED;
- return http->status;
- }
- }
- internal->request_sent = 1;
- return http->status;
- }
-
- // check if socket is ready for recv
- fd_set sockets_to_check;
- FD_ZERO( &sockets_to_check );
- #pragma warning( push )
- #pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect
- FD_SET( internal->socket, &sockets_to_check );
- #pragma warning( pop )
- struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
- while( select( (int)( internal->socket + 1 ), &sockets_to_check, NULL, NULL, &timeout ) == 1 )
- {
- char buffer[ 4096 ];
- int size = recv( internal->socket, buffer, sizeof( buffer ), 0 );
- if( size == -1 )
- {
- http->status = HTTP_STATUS_FAILED;
- return http->status;
- }
- else if( size > 0 )
- {
- size_t min_size = internal->data_size + size + 1;
- if( internal->data_capacity < min_size )
- {
- internal->data_capacity *= 2;
- if( internal->data_capacity < min_size ) internal->data_capacity = min_size;
- void* new_data = HTTP_MALLOC( memctx, internal->data_capacity );
- memcpy( new_data, internal->data, internal->data_size );
- HTTP_FREE( memctx, internal->data );
- internal->data = new_data;
- }
- memcpy( (void*)( ( (uintptr_t) internal->data ) + internal->data_size ), buffer, (size_t) size );
- internal->data_size += size;
- }
- else if( size == 0 )
- {
- char const* status_line = (char const*) internal->data;
-
- int header_size = 0;
- char const* header_end = strstr( status_line, "\r\n\r\n" );
- if( header_end )
- {
- header_end += 4;
- header_size = (int)( header_end - status_line );
- }
- else
- {
- http->status = HTTP_STATUS_FAILED;
- return http->status;
- }
-
- // skip http version
- status_line = strchr( status_line, ' ' );
- if( !status_line )
- {
- http->status = HTTP_STATUS_FAILED;
- return http->status;
- }
- ++status_line;
-
- // extract status code
- char status_code[ 16 ];
- char const* status_code_end = strchr( status_line, ' ' );
- if( !status_code_end )
- {
- http->status = HTTP_STATUS_FAILED;
- return http->status;
- }
- memcpy( status_code, status_line, (size_t)( status_code_end - status_line ) );
- status_code[ status_code_end - status_line ] = 0;
- status_line = status_code_end + 1;
- http->status_code = atoi( status_code );
-
- // extract reason phrase
- char const* reason_phrase_end = strstr( status_line, "\r\n" );
- if( !reason_phrase_end )
- {
- http->status = HTTP_STATUS_FAILED;
- return http->status;
- }
- size_t reason_phrase_len = (size_t)( reason_phrase_end - status_line );
- if( reason_phrase_len >= sizeof( internal->reason_phrase ) )
- reason_phrase_len = sizeof( internal->reason_phrase ) - 1;
- memcpy( internal->reason_phrase, status_line, reason_phrase_len );
- internal->reason_phrase[ reason_phrase_len ] = 0;
- status_line = reason_phrase_end + 1;
-
- // extract content type
- char const* content_type_start = strstr( status_line, "Content-Type: " );
- if( content_type_start )
- {
- content_type_start += strlen( "Content-Type: " );
- char const* content_type_end = strstr( content_type_start, "\r\n" );
- if( content_type_end )
- {
- size_t content_type_len = (size_t)( content_type_end - content_type_start );
- if( content_type_len >= sizeof( internal->content_type ) )
- content_type_len = sizeof( internal->content_type ) - 1;
- memcpy( internal->content_type, content_type_start, content_type_len );
- internal->content_type[ content_type_len ] = 0;
- }
- }
-
- http->status = http->status_code < 300 ? HTTP_STATUS_COMPLETED : HTTP_STATUS_FAILED;
- http->response_data = (void*)( ( (uintptr_t) internal->data ) + header_size );
- http->response_size = internal->data_size - header_size;
-
- // add an extra zero after the received data, but don't modify the size, so ascii results can be used as
- // a zero terminated string. the size returned will be the string without this extra zero terminator.
- ( (char*)http->response_data )[ http->response_size ] = 0;
- return http->status;
- }
- }
-
- return http->status;
- }
-
-
-void http_release( http_t* http )
- {
- http_internal_t* internal = (http_internal_t*) http;
- #ifdef _WIN32
- closesocket( internal->socket );
- #else
- close( internal->socket );
- #endif
-
- if( internal->request_header_large) HTTP_FREE( memctx, internal->request_header_large );
- HTTP_FREE( memctx, internal->data );
- HTTP_FREE( memctx, internal );
- #ifdef _WIN32
- WSACleanup();
- #endif
- }
-
-
-#endif /* HTTP_IMPLEMENTATION */
-
-/*
-revision history:
- 1.0 first released version
-*/
-
-/*
-------------------------------------------------------------------------------
-
-This software is available under 2 licenses - you may choose the one you like.
-
-------------------------------------------------------------------------------
-
-ALTERNATIVE A - MIT License
-
-Copyright (c) 2016 Mattias Gustavsson
-
-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.
-
-------------------------------------------------------------------------------
-
-ALTERNATIVE B - Public Domain (www.unlicense.org)
-
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-software, either in source code form or as a compiled binary, for any purpose,
-commercial or non-commercial, and by any means.
-
-In jurisdictions that recognize copyright laws, the author or authors of this
-software dedicate any and all copyright interest in the software to the public
-domain. We make this dedication for the benefit of the public at large and to
-the detriment of our heirs and successors. We intend this dedication to be an
-overt act of relinquishment in perpetuity of all present and future rights to
-this software under copyright law.
-
-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 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/ini.c b/ini.c
deleted file mode 100644
index 509952dc92f0..000000000000
--- a/ini.c
+++ /dev/null
@@ -1,304 +0,0 @@
-/* inih -- simple .INI file parser
-
-SPDX-License-Identifier: BSD-3-Clause
-
-Copyright (C) 2009-2020, Ben Hoyt
-
-inih is released under the New BSD license (see LICENSE.txt). Go to the project
-home page for more info:
-
-https://github.com/benhoyt/inih
-
-*/
-
-#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
-#define _CRT_SECURE_NO_WARNINGS
-#endif
-
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-
-#include "ini.h"
-
-#if !INI_USE_STACK
-#if INI_CUSTOM_ALLOCATOR
-#include <stddef.h>
-void* ini_malloc(size_t size);
-void ini_free(void* ptr);
-void* ini_realloc(void* ptr, size_t size);
-#else
-#include <stdlib.h>
-#define ini_malloc malloc
-#define ini_free free
-#define ini_realloc realloc
-#endif
-#endif
-
-#define MAX_SECTION 50
-#define MAX_NAME 50
-
-/* Used by ini_parse_string() to keep track of string parsing state. */
-typedef struct {
- const char* ptr;
- size_t num_left;
-} ini_parse_string_ctx;
-
-/* Strip whitespace chars off end of given string, in place. Return s. */
-static char* rstrip(char* s)
-{
- char* p = s + strlen(s);
- while (p > s && isspace((unsigned char)(*--p)))
- *p = '\0';
- return s;
-}
-
-/* Return pointer to first non-whitespace char in given string. */
-static char* lskip(const char* s)
-{
- while (*s && isspace((unsigned char)(*s)))
- s++;
- return (char*)s;
-}
-
-/* Return pointer to first char (of chars) or inline comment in given string,
- or pointer to NUL at end of string if neither found. Inline comment must
- be prefixed by a whitespace character to register as a comment. */
-static char* find_chars_or_comment(const char* s, const char* chars)
-{
-#if INI_ALLOW_INLINE_COMMENTS
- int was_space = 0;
- while (*s && (!chars || !strchr(chars, *s)) &&
- !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
- was_space = isspace((unsigned char)(*s));
- s++;
- }
-#else
- while (*s && (!chars || !strchr(chars, *s))) {
- s++;
- }
-#endif
- return (char*)s;
-}
-
-/* Similar to strncpy, but ensures dest (size bytes) is
- NUL-terminated, and doesn't pad with NULs. */
-static char* strncpy0(char* dest, const char* src, size_t size)
-{
- /* Could use strncpy internally, but it causes gcc warnings (see issue #91) */
- size_t i;
- for (i = 0; i < size - 1 && src[i]; i++)
- dest[i] = src[i];
- dest[i] = '\0';
- return dest;
-}
-
-/* See documentation in header file. */
-int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
- void* user)
-{
- /* Uses a fair bit of stack (use heap instead if you need to) */
-#if INI_USE_STACK
- char line[INI_MAX_LINE];
- size_t max_line = INI_MAX_LINE;
-#else
- char* line;
- size_t max_line = INI_INITIAL_ALLOC;
-#endif
-#if INI_ALLOW_REALLOC && !INI_USE_STACK
- char* new_line;
- size_t offset;
-#endif
- char section[MAX_SECTION] = "";
- char prev_name[MAX_NAME] = "";
-
- char* start;
- char* end;
- char* name;
- char* value;
- int lineno = 0;
- int error = 0;
-
-#if !INI_USE_STACK
- line = (char*)ini_malloc(INI_INITIAL_ALLOC);
- if (!line) {
- return -2;
- }
-#endif
-
-#if INI_HANDLER_LINENO
-#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
-#else
-#define HANDLER(u, s, n, v) handler(u, s, n, v)
-#endif
-
- /* Scan through stream line by line */
- while (reader(line, (int)max_line, stream) != NULL) {
-#if INI_ALLOW_REALLOC && !INI_USE_STACK
- offset = strlen(line);
- while (offset == max_line - 1 && line[offset - 1] != '\n') {
- max_line *= 2;
- if (max_line > INI_MAX_LINE)
- max_line = INI_MAX_LINE;
- new_line = ini_realloc(line, max_line);
- if (!new_line) {
- ini_free(line);
- return -2;
- }
- line = new_line;
- if (reader(line + offset, (int)(max_line - offset), stream) == NULL)
- break;
- if (max_line >= INI_MAX_LINE)
- break;
- offset += strlen(line + offset);
- }
-#endif
-
- lineno++;
-
- start = line;
-#if INI_ALLOW_BOM
- if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
- (unsigned char)start[1] == 0xBB &&
- (unsigned char)start[2] == 0xBF) {
- start += 3;
- }
-#endif
- start = lskip(rstrip(start));
-
- if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
- /* Start-of-line comment */
- }
-#if INI_ALLOW_MULTILINE
- else if (*prev_name && *start && start > line) {
-#if INI_ALLOW_INLINE_COMMENTS
- end = find_chars_or_comment(start, NULL);
- if (*end)
- *end = '\0';
- rstrip(start);
-#endif
- /* Non-blank line with leading whitespace, treat as continuation
- of previous name's value (as per Python configparser). */
- if (!HANDLER(user, section, prev_name, start) && !error)
- error = lineno;
- }
-#endif
- else if (*start == '[') {
- /* A "[section]" line */
- end = find_chars_or_comment(start + 1, "]");
- if (*end == ']') {
- *end = '\0';
- strncpy0(section, start + 1, sizeof(section));
- *prev_name = '\0';
-#if INI_CALL_HANDLER_ON_NEW_SECTION
- if (!HANDLER(user, section, NULL, NULL) && !error)
- error = lineno;
-#endif
- }
- else if (!error) {
- /* No ']' found on section line */
- error = lineno;
- }
- }
- else if (*start) {
- /* Not a comment, must be a name[=:]value pair */
- end = find_chars_or_comment(start, "=:");
- if (*end == '=' || *end == ':') {
- *end = '\0';
- name = rstrip(start);
- value = end + 1;
-#if INI_ALLOW_INLINE_COMMENTS
- end = find_chars_or_comment(value, NULL);
- if (*end)
- *end = '\0';
-#endif
- value = lskip(value);
- rstrip(value);
-
- /* Valid name[=:]value pair found, call handler */
- strncpy0(prev_name, name, sizeof(prev_name));
- if (!HANDLER(user, section, name, value) && !error)
- error = lineno;
- }
- else if (!error) {
- /* No '=' or ':' found on name[=:]value line */
-#if INI_ALLOW_NO_VALUE
- *end = '\0';
- name = rstrip(start);
- if (!HANDLER(user, section, name, NULL) && !error)
- error = lineno;
-#else
- error = lineno;
-#endif
- }
- }
-
-#if INI_STOP_ON_FIRST_ERROR
- if (error)
- break;
-#endif
- }
-
-#if !INI_USE_STACK
- ini_free(line);
-#endif
-
- return error;
-}
-
-/* See documentation in header file. */
-int ini_parse_file(FILE* file, ini_handler handler, void* user)
-{
- return ini_parse_stream((ini_reader)fgets, file, handler, user);
-}
-
-/* See documentation in header file. */
-int ini_parse(const char* filename, ini_handler handler, void* user)
-{
- FILE* file;
- int error;
-
- file = fopen(filename, "r");
- if (!file)
- return -1;
- error = ini_parse_file(file, handler, user);
- fclose(file);
- return error;
-}
-
-/* An ini_reader function to read the next line from a string buffer. This
- is the fgets() equivalent used by ini_parse_string(). */
-static char* ini_reader_string(char* str, int num, void* stream) {
- ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
- const char* ctx_ptr = ctx->ptr;
- size_t ctx_num_left = ctx->num_left;
- char* strp = str;
- char c;
-
- if (ctx_num_left == 0 || num < 2)
- return NULL;
-
- while (num > 1 && ctx_num_left != 0) {
- c = *ctx_ptr++;
- ctx_num_left--;
- *strp++ = c;
- if (c == '\n')
- break;
- num--;
- }
-
- *strp = '\0';
- ctx->ptr = ctx_ptr;
- ctx->num_left = ctx_num_left;
- return str;
-}
-
-/* See documentation in header file. */
-int ini_parse_string(const char* string, ini_handler handler, void* user) {
- ini_parse_string_ctx ctx;
-
- ctx.ptr = string;
- ctx.num_left = strlen(string);
- return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
- user);
-}
diff --git a/ini.h b/ini.h
deleted file mode 100644
index d1a2ba825a7a..000000000000
--- a/ini.h
+++ /dev/null
@@ -1,178 +0,0 @@
-/* inih -- simple .INI file parser
-
-SPDX-License-Identifier: BSD-3-Clause
-
-Copyright (C) 2009-2020, Ben Hoyt
-
-inih is released under the New BSD license (see LICENSE.txt). Go to the project
-home page for more info:
-
-https://github.com/benhoyt/inih
-
-*/
-
-#ifndef INI_H
-#define INI_H
-
-/* Make this header file easier to include in C++ code */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdio.h>
-
-/* Nonzero if ini_handler callback should accept lineno parameter. */
-#ifndef INI_HANDLER_LINENO
-#define INI_HANDLER_LINENO 0
-#endif
-
-/* Visibility symbols, required for Windows DLLs */
-#ifndef INI_API
-#if defined _WIN32 || defined __CYGWIN__
-# ifdef INI_SHARED_LIB
-# ifdef INI_SHARED_LIB_BUILDING
-# define INI_API __declspec(dllexport)
-# else
-# define INI_API __declspec(dllimport)
-# endif
-# else
-# define INI_API
-# endif
-#else
-# if defined(__GNUC__) && __GNUC__ >= 4
-# define INI_API __attribute__ ((visibility ("default")))
-# else
-# define INI_API
-# endif
-#endif
-#endif
-
-/* Typedef for prototype of handler function. */
-#if INI_HANDLER_LINENO
-typedef int (*ini_handler)(void* user, const char* section,
- const char* name, const char* value,
- int lineno);
-#else
-typedef int (*ini_handler)(void* user, const char* section,
- const char* name, const char* value);
-#endif
-
-/* Typedef for prototype of fgets-style reader function. */
-typedef char* (*ini_reader)(char* str, int num, void* stream);
-
-/* Parse given INI-style file. May have [section]s, name=value pairs
- (whitespace stripped), and comments starting with ';' (semicolon). Section
- is "" if name=value pair parsed before any section heading. name:value
- pairs are also supported as a concession to Python's configparser.
-
- For each name=value pair parsed, call handler function with given user
- pointer as well as section, name, and value (data only valid for duration
- of handler call). Handler should return nonzero on success, zero on error.
-
- Returns 0 on success, line number of first error on parse error (doesn't
- stop on first error), -1 on file open error, or -2 on memory allocation
- error (only when INI_USE_STACK is zero).
-*/
-INI_API int ini_parse(const char* filename, ini_handler handler, void* user);
-
-/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
- close the file when it's finished -- the caller must do that. */
-INI_API int ini_parse_file(FILE* file, ini_handler handler, void* user);
-
-/* Same as ini_parse(), but takes an ini_reader function pointer instead of
- filename. Used for implementing custom or string-based I/O (see also
- ini_parse_string). */
-INI_API int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
- void* user);
-
-/* Same as ini_parse(), but takes a zero-terminated string with the INI data
-instead of a file. Useful for parsing INI data from a network socket or
-already in memory. */
-INI_API int ini_parse_string(const char* string, ini_handler handler, void* user);
-
-/* Nonzero to allow multi-line value parsing, in the style of Python's
- configparser. If allowed, ini_parse() will call the handler with the same
- name for each subsequent line parsed. */
-#ifndef INI_ALLOW_MULTILINE
-#define INI_ALLOW_MULTILINE 1
-#endif
-
-/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
- the file. See https://github.com/benhoyt/inih/issues/21 */
-#ifndef INI_ALLOW_BOM
-#define INI_ALLOW_BOM 1
-#endif
-
-/* Chars that begin a start-of-line comment. Per Python configparser, allow
- both ; and # comments at the start of a line by default. */
-#ifndef INI_START_COMMENT_PREFIXES
-#define INI_START_COMMENT_PREFIXES ";#"
-#endif
-
-/* Nonzero to allow inline comments (with valid inline comment characters
- specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
- Python 3.2+ configparser behaviour. */
-#ifndef INI_ALLOW_INLINE_COMMENTS
-#define INI_ALLOW_INLINE_COMMENTS 1
-#endif
-#ifndef INI_INLINE_COMMENT_PREFIXES
-#define INI_INLINE_COMMENT_PREFIXES ";"
-#endif
-
-/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
-#ifndef INI_USE_STACK
-#define INI_USE_STACK 1
-#endif
-
-/* Maximum line length for any line in INI file (stack or heap). Note that
- this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
-#ifndef INI_MAX_LINE
-#define INI_MAX_LINE 200
-#endif
-
-/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
- fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
- zero. */
-#ifndef INI_ALLOW_REALLOC
-#define INI_ALLOW_REALLOC 0
-#endif
-
-/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
- is zero. */
-#ifndef INI_INITIAL_ALLOC
-#define INI_INITIAL_ALLOC 200
-#endif
-
-/* Stop parsing on first error (default is to keep parsing). */
-#ifndef INI_STOP_ON_FIRST_ERROR
-#define INI_STOP_ON_FIRST_ERROR 0
-#endif
-
-/* Nonzero to call the handler at the start of each new section (with
- name and value NULL). Default is to only call the handler on
- each name=value pair. */
-#ifndef INI_CALL_HANDLER_ON_NEW_SECTION
-#define INI_CALL_HANDLER_ON_NEW_SECTION 0
-#endif
-
-/* Nonzero to allow a name without a value (no '=' or ':' on the line) and
- call the handler with value NULL in this case. Default is to treat
- no-value lines as an error. */
-#ifndef INI_ALLOW_NO_VALUE
-#define INI_ALLOW_NO_VALUE 0
-#endif
-
-/* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory
- allocation functions (INI_USE_STACK must also be 0). These functions must
- have the same signatures as malloc/free/realloc and behave in a similar
- way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */
-#ifndef INI_CUSTOM_ALLOCATOR
-#define INI_CUSTOM_ALLOCATOR 0
-#endif
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* INI_H */
diff --git a/libdeflate.h b/libdeflate.h
deleted file mode 100644
index 382d895de89c..000000000000
--- a/libdeflate.h
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * libdeflate.h - public header for libdeflate
- */
-
-#ifndef LIBDEFLATE_H
-#define LIBDEFLATE_H
-
-#include <stddef.h>
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define LIBDEFLATE_VERSION_MAJOR 1
-#define LIBDEFLATE_VERSION_MINOR 18
-#define LIBDEFLATE_VERSION_STRING "1.18"
-
-/*
- * Users of libdeflate.dll on Windows can define LIBDEFLATE_DLL to cause
- * __declspec(dllimport) to be used. This should be done when it's easy to do.
- * Otherwise it's fine to skip it, since it is a very minor performance
- * optimization that is irrelevant for most use cases of libdeflate.
- */
-#ifndef LIBDEFLATEAPI
-# if defined(LIBDEFLATE_DLL) && (defined(_WIN32) || defined(__CYGWIN__))
-# define LIBDEFLATEAPI __declspec(dllimport)
-# else
-# define LIBDEFLATEAPI
-# endif
-#endif
-
-/* ========================================================================== */
-/* Compression */
-/* ========================================================================== */
-
-struct libdeflate_compressor;
-struct libdeflate_options;
-
-/*
- * libdeflate_alloc_compressor() allocates a new compressor that supports
- * DEFLATE, zlib, and gzip compression. 'compression_level' is the compression
- * level on a zlib-like scale but with a higher maximum value (1 = fastest, 6 =
- * medium/default, 9 = slow, 12 = slowest). Level 0 is also supported and means
- * "no compression", specifically "create a valid stream, but only emit
- * uncompressed blocks" (this will expand the data slightly).
- *
- * The return value is a pointer to the new compressor, or NULL if out of memory
- * or if the compression level is invalid (i.e. outside the range [0, 12]).
- *
- * Note: for compression, the sliding window size is defined at compilation time
- * to 32768, the largest size permissible in the DEFLATE format. It cannot be
- * changed at runtime.
- *
- * A single compressor is not safe to use by multiple threads concurrently.
- * However, different threads may use different compressors concurrently.
- */
-LIBDEFLATEAPI struct libdeflate_compressor *
-libdeflate_alloc_compressor(int compression_level);
-
-/*
- * Like libdeflate_alloc_compressor(), but adds the 'options' argument.
- */
-LIBDEFLATEAPI struct libdeflate_compressor *
-libdeflate_alloc_compressor_ex(int compression_level,
- const struct libdeflate_options *options);
-
-/*
- * libdeflate_deflate_compress() performs raw DEFLATE compression on a buffer of
- * data. It attempts to compress 'in_nbytes' bytes of data located at 'in' and
- * write the result to 'out', which has space for 'out_nbytes_avail' bytes. The
- * return value is the compressed size in bytes, or 0 if the data could not be
- * compressed to 'out_nbytes_avail' bytes or fewer (but see note below).
- *
- * If compression is successful, then the output data is guaranteed to be a
- * valid DEFLATE stream that decompresses to the input data. No other
- * guarantees are made about the output data. Notably, different versions of
- * libdeflate can produce different compressed data for the same uncompressed
- * data, even at the same compression level. Do ***NOT*** do things like
- * writing tests that compare compressed data to a golden output, as this can
- * break when libdeflate is updated. (This property isn't specific to
- * libdeflate; the same is true for zlib and other compression libraries too.)
- */
-LIBDEFLATEAPI size_t
-libdeflate_deflate_compress(struct libdeflate_compressor *compressor,
- const void *in, size_t in_nbytes,
- void *out, size_t out_nbytes_avail);
-
-/*
- * libdeflate_deflate_compress_bound() returns a worst-case upper bound on the
- * number of bytes of compressed data that may be produced by compressing any
- * buffer of length less than or equal to 'in_nbytes' using
- * libdeflate_deflate_compress() with the specified compressor. This bound will
- * necessarily be a number greater than or equal to 'in_nbytes'. It may be an
- * overestimate of the true upper bound. The return value is guaranteed to be
- * the same for all invocations with the same compressor and same 'in_nbytes'.
- *
- * As a special case, 'compressor' may be NULL. This causes the bound to be
- * taken across *any* libdeflate_compressor that could ever be allocated with
- * this build of the library, with any options.
- *
- * Note that this function is not necessary in many applications. With
- * block-based compression, it is usually preferable to separately store the
- * uncompressed size of each block and to store any blocks that did not compress
- * to less than their original size uncompressed. In that scenario, there is no
- * need to know the worst-case compressed size, since the maximum number of
- * bytes of compressed data that may be used would always be one less than the
- * input length. You can just pass a buffer of that size to
- * libdeflate_deflate_compress() and store the data uncompressed if
- * libdeflate_deflate_compress() returns 0, indicating that the compressed data
- * did not fit into the provided output buffer.
- */
-LIBDEFLATEAPI size_t
-libdeflate_deflate_compress_bound(struct libdeflate_compressor *compressor,
- size_t in_nbytes);
-
-/*
- * Like libdeflate_deflate_compress(), but uses the zlib wrapper format instead
- * of raw DEFLATE.
- */
-LIBDEFLATEAPI size_t
-libdeflate_zlib_compress(struct libdeflate_compressor *compressor,
- const void *in, size_t in_nbytes,
- void *out, size_t out_nbytes_avail);
-
-/*
- * Like libdeflate_deflate_compress_bound(), but assumes the data will be
- * compressed with libdeflate_zlib_compress() rather than with
- * libdeflate_deflate_compress().
- */
-LIBDEFLATEAPI size_t
-libdeflate_zlib_compress_bound(struct libdeflate_compressor *compressor,
- size_t in_nbytes);
-
-/*
- * Like libdeflate_deflate_compress(), but uses the gzip wrapper format instead
- * of raw DEFLATE.
- */
-LIBDEFLATEAPI size_t
-libdeflate_gzip_compress(struct libdeflate_compressor *compressor,
- const void *in, size_t in_nbytes,
- void *out, size_t out_nbytes_avail);
-
-/*
- * Like libdeflate_deflate_compress_bound(), but assumes the data will be
- * compressed with libdeflate_gzip_compress() rather than with
- * libdeflate_deflate_compress().
- */
-LIBDEFLATEAPI size_t
-libdeflate_gzip_compress_bound(struct libdeflate_compressor *compressor,
- size_t in_nbytes);
-
-/*
- * libdeflate_free_compressor() frees a compressor that was allocated with
- * libdeflate_alloc_compressor(). If a NULL pointer is passed in, no action is
- * taken.
- */
-LIBDEFLATEAPI void
-libdeflate_free_compressor(struct libdeflate_compressor *compressor);
-
-/* ========================================================================== */
-/* Decompression */
-/* ========================================================================== */
-
-struct libdeflate_decompressor;
-struct libdeflate_options;
-
-/*
- * libdeflate_alloc_decompressor() allocates a new decompressor that can be used
- * for DEFLATE, zlib, and gzip decompression. The return value is a pointer to
- * the new decompressor, or NULL if out of memory.
- *
- * This function takes no parameters, and the returned decompressor is valid for
- * decompressing data that was compressed at any compression level and with any
- * sliding window size.
- *
- * A single decompressor is not safe to use by multiple threads concurrently.
- * However, different threads may use different decompressors concurrently.
- */
-LIBDEFLATEAPI struct libdeflate_decompressor *
-libdeflate_alloc_decompressor(void);
-
-/*
- * Like libdeflate_alloc_decompressor(), but adds the 'options' argument.
- */
-LIBDEFLATEAPI struct libdeflate_decompressor *
-libdeflate_alloc_decompressor_ex(const struct libdeflate_options *options);
-
-/*
- * Result of a call to libdeflate_deflate_decompress(),
- * libdeflate_zlib_decompress(), or libdeflate_gzip_decompress().
- */
-enum libdeflate_result {
- /* Decompression was successful. */
- LIBDEFLATE_SUCCESS = 0,
-
- /* Decompression failed because the compressed data was invalid,
- * corrupt, or otherwise unsupported. */
- LIBDEFLATE_BAD_DATA = 1,
-
- /* A NULL 'actual_out_nbytes_ret' was provided, but the data would have
- * decompressed to fewer than 'out_nbytes_avail' bytes. */
- LIBDEFLATE_SHORT_OUTPUT = 2,
-
- /* The data would have decompressed to more than 'out_nbytes_avail'
- * bytes. */
- LIBDEFLATE_INSUFFICIENT_SPACE = 3,
-};
-
-/*
- * libdeflate_deflate_decompress() decompresses a DEFLATE stream from the buffer
- * 'in' with compressed size up to 'in_nbytes' bytes. The uncompressed data is
- * written to 'out', a buffer with size 'out_nbytes_avail' bytes. If
- * decompression succeeds, then 0 (LIBDEFLATE_SUCCESS) is returned. Otherwise,
- * a nonzero result code such as LIBDEFLATE_BAD_DATA is returned, and the
- * contents of the output buffer are undefined.
- *
- * Decompression stops at the end of the DEFLATE stream (as indicated by the
- * BFINAL flag), even if it is actually shorter than 'in_nbytes' bytes.
- *
- * libdeflate_deflate_decompress() can be used in cases where the actual
- * uncompressed size is known (recommended) or unknown (not recommended):
- *
- * - If the actual uncompressed size is known, then pass the actual
- * uncompressed size as 'out_nbytes_avail' and pass NULL for
- * 'actual_out_nbytes_ret'. This makes libdeflate_deflate_decompress() fail
- * with LIBDEFLATE_SHORT_OUTPUT if the data decompressed to fewer than the
- * specified number of bytes.
- *
- * - If the actual uncompressed size is unknown, then provide a non-NULL
- * 'actual_out_nbytes_ret' and provide a buffer with some size
- * 'out_nbytes_avail' that you think is large enough to hold all the
- * uncompressed data. In this case, if the data decompresses to less than
- * or equal to 'out_nbytes_avail' bytes, then
- * libdeflate_deflate_decompress() will write the actual uncompressed size
- * to *actual_out_nbytes_ret and return 0 (LIBDEFLATE_SUCCESS). Otherwise,
- * it will return LIBDEFLATE_INSUFFICIENT_SPACE if the provided buffer was
- * not large enough but no other problems were encountered, or another
- * nonzero result code if decompression failed for another reason.
- */
-LIBDEFLATEAPI enum libdeflate_result
-libdeflate_deflate_decompress(struct libdeflate_decompressor *decompressor,
- const void *in, size_t in_nbytes,
- void *out, size_t out_nbytes_avail,
- size_t *actual_out_nbytes_ret);
-
-/*
- * Like libdeflate_deflate_decompress(), but adds the 'actual_in_nbytes_ret'
- * argument. If decompression succeeds and 'actual_in_nbytes_ret' is not NULL,
- * then the actual compressed size of the DEFLATE stream (aligned to the next
- * byte boundary) is written to *actual_in_nbytes_ret.
- */
-LIBDEFLATEAPI enum libdeflate_result
-libdeflate_deflate_decompress_ex(struct libdeflate_decompressor *decompressor,
- const void *in, size_t in_nbytes,
- void *out, size_t out_nbytes_avail,
- size_t *actual_in_nbytes_ret,
- size_t *actual_out_nbytes_ret);
-
-/*
- * Like libdeflate_deflate_decompress(), but assumes the zlib wrapper format
- * instead of raw DEFLATE.
- *
- * Decompression will stop at the end of the zlib stream, even if it is shorter
- * than 'in_nbytes'. If you need to know exactly where the zlib stream ended,
- * use libdeflate_zlib_decompress_ex().
- */
-LIBDEFLATEAPI enum libdeflate_result
-libdeflate_zlib_decompress(struct libdeflate_decompressor *decompressor,
- const void *in, size_t in_nbytes,
- void *out, size_t out_nbytes_avail,
- size_t *actual_out_nbytes_ret);
-
-/*
- * Like libdeflate_zlib_decompress(), but adds the 'actual_in_nbytes_ret'
- * argument. If 'actual_in_nbytes_ret' is not NULL and the decompression
- * succeeds (indicating that the first zlib-compressed stream in the input
- * buffer was decompressed), then the actual number of input bytes consumed is
- * written to *actual_in_nbytes_ret.
- */
-LIBDEFLATEAPI enum libdeflate_result
-libdeflate_zlib_decompress_ex(struct libdeflate_decompressor *decompressor,
- const void *in, size_t in_nbytes,
- void *out, size_t out_nbytes_avail,
- size_t *actual_in_nbytes_ret,
- size_t *actual_out_nbytes_ret);
-
-/*
- * Like libdeflate_deflate_decompress(), but assumes the gzip wrapper format
- * instead of raw DEFLATE.
- *
- * If multiple gzip-compressed members are concatenated, then only the first
- * will be decompressed. Use libdeflate_gzip_decompress_ex() if you need
- * multi-member support.
- */
-LIBDEFLATEAPI enum libdeflate_result
-libdeflate_gzip_decompress(struct libdeflate_decompressor *decompressor,
- const void *in, size_t in_nbytes,
- void *out, size_t out_nbytes_avail,
- size_t *actual_out_nbytes_ret);
-
-/*
- * Like libdeflate_gzip_decompress(), but adds the 'actual_in_nbytes_ret'
- * argument. If 'actual_in_nbytes_ret' is not NULL and the decompression
- * succeeds (indicating that the first gzip-compressed member in the input
- * buffer was decompressed), then the actual number of input bytes consumed is
- * written to *actual_in_nbytes_ret.
- */
-LIBDEFLATEAPI enum libdeflate_result
-libdeflate_gzip_decompress_ex(struct libdeflate_decompressor *decompressor,
- const void *in, size_t in_nbytes,
- void *out, size_t out_nbytes_avail,
- size_t *actual_in_nbytes_ret,
- size_t *actual_out_nbytes_ret);
-
-/*
- * libdeflate_free_decompressor() frees a decompressor that was allocated with
- * libdeflate_alloc_decompressor(). If a NULL pointer is passed in, no action
- * is taken.
- */
-LIBDEFLATEAPI void
-libdeflate_free_decompressor(struct libdeflate_decompressor *decompressor);
-
-/* ========================================================================== */
-/* Checksums */
-/* ========================================================================== */
-
-/*
- * libdeflate_adler32() updates a running Adler-32 checksum with 'len' bytes of
- * data and returns the updated checksum. When starting a new checksum, the
- * required initial value for 'adler' is 1. This value is also returned when
- * 'buffer' is specified as NULL.
- */
-LIBDEFLATEAPI uint32_t
-libdeflate_adler32(uint32_t adler, const void *buffer, size_t len);
-
-
-/*
- * libdeflate_crc32() updates a running CRC-32 checksum with 'len' bytes of data
- * and returns the updated checksum. When starting a new checksum, the required
- * initial value for 'crc' is 0. This value is also returned when 'buffer' is
- * specified as NULL.
- */
-LIBDEFLATEAPI uint32_t
-libdeflate_crc32(uint32_t crc, const void *buffer, size_t len);
-
-/* ========================================================================== */
-/* Custom memory allocator */
-/* ========================================================================== */
-
-/*
- * Install a custom memory allocator which libdeflate will use for all memory
- * allocations by default. 'malloc_func' is a function that must behave like
- * malloc(), and 'free_func' is a function that must behave like free().
- *
- * The per-(de)compressor custom memory allocator that can be specified in
- * 'struct libdeflate_options' takes priority over this.
- *
- * This doesn't affect the free() function that will be used to free
- * (de)compressors that were already in existence when this is called.
- */
-LIBDEFLATEAPI void
-libdeflate_set_memory_allocator(void *(*malloc_func)(size_t),
- void (*free_func)(void *));
-
-/*
- * Advanced options. This is the options structure that
- * libdeflate_alloc_compressor_ex() and libdeflate_alloc_decompressor_ex()
- * require. Most users won't need this and should just use the non-"_ex"
- * functions instead. If you do need this, it should be initialized like this:
- *
- * struct libdeflate_options options;
- *
- * memset(&options, 0, sizeof(options));
- * options.sizeof_options = sizeof(options);
- * // Then set the fields that you need to override the defaults for.
- */
-struct libdeflate_options {
-
- /*
- * This field must be set to the struct size. This field exists for
- * extensibility, so that fields can be appended to this struct in
- * future versions of libdeflate while still supporting old binaries.
- */
- size_t sizeof_options;
-
- /*
- * An optional custom memory allocator to use for this (de)compressor.
- * 'malloc_func' must be a function that behaves like malloc(), and
- * 'free_func' must be a function that behaves like free().
- *
- * This is useful in cases where a process might have multiple users of
- * libdeflate who want to use different memory allocators. For example,
- * a library might want to use libdeflate with a custom memory allocator
- * without interfering with user code that might use libdeflate too.
- *
- * This takes priority over the "global" memory allocator (which by
- * default is malloc() and free(), but can be changed by
- * libdeflate_set_memory_allocator()). Moreover, libdeflate will never
- * call the "global" memory allocator if a per-(de)compressor custom
- * allocator is always given.
- */
- void *(*malloc_func)(size_t);
- void (*free_func)(void *);
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LIBDEFLATE_H */
diff --git a/lodepng.cpp b/lodepng.cpp
deleted file mode 100644
index 6ab9694903e4..000000000000
--- a/lodepng.cpp
+++ /dev/null
@@ -1,6991 +0,0 @@
-/*
-LodePNG version 20230410
-
-Copyright (c) 2005-2023 Lode Vandevenne
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source
- distribution.
-*/
-
-/*
-The manual and changelog are in the header file "lodepng.h"
-Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C.
-*/
-
-#include "lodepng.h"
-
-#ifdef LODEPNG_COMPILE_DISK
-#include <limits.h> /* LONG_MAX */
-#include <stdio.h> /* file handling */
-#endif /* LODEPNG_COMPILE_DISK */
-
-#ifdef LODEPNG_COMPILE_ALLOCATORS
-#include <stdlib.h> /* allocations */
-#endif /* LODEPNG_COMPILE_ALLOCATORS */
-
-#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/
-#pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/
-#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
-#endif /*_MSC_VER */
-
-const char* LODEPNG_VERSION_STRING = "20230410";
-
-/*
-This source file is divided into the following large parts. The code sections
-with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way.
--Tools for C and common code for PNG and Zlib
--C Code for Zlib (huffman, deflate, ...)
--C Code for PNG (file format chunks, adam7, PNG filters, color conversions, ...)
--The C++ wrapper around all of the above
-*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* // Tools for C, and common code for PNG and Zlib. // */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*The malloc, realloc and free functions defined here with "lodepng_" in front
-of the name, so that you can easily change them to others related to your
-platform if needed. Everything else in the code calls these. Pass
--DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler, or comment out
-#define LODEPNG_COMPILE_ALLOCATORS in the header, to disable the ones here and
-define them in your own project's source files without needing to change
-lodepng source code. Don't forget to remove "static" if you copypaste them
-from here.*/
-
-#ifdef LODEPNG_COMPILE_ALLOCATORS
-static void* lodepng_malloc(size_t size) {
-#ifdef LODEPNG_MAX_ALLOC
- if(size > LODEPNG_MAX_ALLOC) return 0;
-#endif
- return malloc(size);
-}
-
-/* NOTE: when realloc returns NULL, it leaves the original memory untouched */
-static void* lodepng_realloc(void* ptr, size_t new_size) {
-#ifdef LODEPNG_MAX_ALLOC
- if(new_size > LODEPNG_MAX_ALLOC) return 0;
-#endif
- return realloc(ptr, new_size);
-}
-
-static void lodepng_free(void* ptr) {
- free(ptr);
-}
-#else /*LODEPNG_COMPILE_ALLOCATORS*/
-/* TODO: support giving additional void* payload to the custom allocators */
-void* lodepng_malloc(size_t size);
-void* lodepng_realloc(void* ptr, size_t new_size);
-void lodepng_free(void* ptr);
-#endif /*LODEPNG_COMPILE_ALLOCATORS*/
-
-/* convince the compiler to inline a function, for use when this measurably improves performance */
-/* inline is not available in C90, but use it when supported by the compiler */
-#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (defined(__cplusplus) && (__cplusplus >= 199711L))
-#define LODEPNG_INLINE inline
-#else
-#define LODEPNG_INLINE /* not available */
-#endif
-
-/* restrict is not available in C90, but use it when supported by the compiler */
-#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\
- (defined(_MSC_VER) && (_MSC_VER >= 1400)) || \
- (defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus))
-#define LODEPNG_RESTRICT __restrict
-#else
-#define LODEPNG_RESTRICT /* not available */
-#endif
-
-/* Replacements for C library functions such as memcpy and strlen, to support platforms
-where a full C library is not available. The compiler can recognize them and compile
-to something as fast. */
-
-static void lodepng_memcpy(void* LODEPNG_RESTRICT dst,
- const void* LODEPNG_RESTRICT src, size_t size) {
- size_t i;
- for(i = 0; i < size; i++) ((char*)dst)[i] = ((const char*)src)[i];
-}
-
-static void lodepng_memset(void* LODEPNG_RESTRICT dst,
- int value, size_t num) {
- size_t i;
- for(i = 0; i < num; i++) ((char*)dst)[i] = (char)value;
-}
-
-/* does not check memory out of bounds, do not use on untrusted data */
-static size_t lodepng_strlen(const char* a) {
- const char* orig = a;
- /* avoid warning about unused function in case of disabled COMPILE... macros */
- (void)(&lodepng_strlen);
- while(*a) a++;
- return (size_t)(a - orig);
-}
-
-#define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b))
-#define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b))
-
-#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER)
-/* Safely check if adding two integers will overflow (no undefined
-behavior, compiler removing the code, etc...) and output result. */
-static int lodepng_addofl(size_t a, size_t b, size_t* result) {
- *result = a + b; /* Unsigned addition is well defined and safe in C90 */
- return *result < a;
-}
-#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER)*/
-
-#ifdef LODEPNG_COMPILE_DECODER
-/* Safely check if multiplying two integers will overflow (no undefined
-behavior, compiler removing the code, etc...) and output result. */
-static int lodepng_mulofl(size_t a, size_t b, size_t* result) {
- *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */
- return (a != 0 && *result / a != b);
-}
-
-#ifdef LODEPNG_COMPILE_ZLIB
-/* Safely check if a + b > c, even if overflow could happen. */
-static int lodepng_gtofl(size_t a, size_t b, size_t c) {
- size_t d;
- if(lodepng_addofl(a, b, &d)) return 1;
- return d > c;
-}
-#endif /*LODEPNG_COMPILE_ZLIB*/
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-
-/*
-Often in case of an error a value is assigned to a variable and then it breaks
-out of a loop (to go to the cleanup phase of a function). This macro does that.
-It makes the error handling code shorter and more readable.
-
-Example: if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83);
-*/
-#define CERROR_BREAK(errorvar, code){\
- errorvar = code;\
- break;\
-}
-
-/*version of CERROR_BREAK that assumes the common case where the error variable is named "error"*/
-#define ERROR_BREAK(code) CERROR_BREAK(error, code)
-
-/*Set error var to the error code, and return it.*/
-#define CERROR_RETURN_ERROR(errorvar, code){\
- errorvar = code;\
- return code;\
-}
-
-/*Try the code, if it returns error, also return the error.*/
-#define CERROR_TRY_RETURN(call){\
- unsigned error = call;\
- if(error) return error;\
-}
-
-/*Set error var to the error code, and return from the void function.*/
-#define CERROR_RETURN(errorvar, code){\
- errorvar = code;\
- return;\
-}
-
-/*
-About uivector, ucvector and string:
--All of them wrap dynamic arrays or text strings in a similar way.
--LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version.
--The string tools are made to avoid problems with compilers that declare things like strncat as deprecated.
--They're not used in the interface, only internally in this file as static functions.
--As with many other structs in this file, the init and cleanup functions serve as ctor and dtor.
-*/
-
-#ifdef LODEPNG_COMPILE_ZLIB
-#ifdef LODEPNG_COMPILE_ENCODER
-/*dynamic vector of unsigned ints*/
-typedef struct uivector {
- unsigned* data;
- size_t size; /*size in number of unsigned longs*/
- size_t allocsize; /*allocated size in bytes*/
-} uivector;
-
-static void uivector_cleanup(void* p) {
- ((uivector*)p)->size = ((uivector*)p)->allocsize = 0;
- lodepng_free(((uivector*)p)->data);
- ((uivector*)p)->data = NULL;
-}
-
-/*returns 1 if success, 0 if failure ==> nothing done*/
-static unsigned uivector_resize(uivector* p, size_t size) {
- size_t allocsize = size * sizeof(unsigned);
- if(allocsize > p->allocsize) {
- size_t newsize = allocsize + (p->allocsize >> 1u);
- void* data = lodepng_realloc(p->data, newsize);
- if(data) {
- p->allocsize = newsize;
- p->data = (unsigned*)data;
- }
- else return 0; /*error: not enough memory*/
- }
- p->size = size;
- return 1; /*success*/
-}
-
-static void uivector_init(uivector* p) {
- p->data = NULL;
- p->size = p->allocsize = 0;
-}
-
-/*returns 1 if success, 0 if failure ==> nothing done*/
-static unsigned uivector_push_back(uivector* p, unsigned c) {
- if(!uivector_resize(p, p->size + 1)) return 0;
- p->data[p->size - 1] = c;
- return 1;
-}
-#endif /*LODEPNG_COMPILE_ENCODER*/
-#endif /*LODEPNG_COMPILE_ZLIB*/
-
-/* /////////////////////////////////////////////////////////////////////////// */
-
-/*dynamic vector of unsigned chars*/
-typedef struct ucvector {
- unsigned char* data;
- size_t size; /*used size*/
- size_t allocsize; /*allocated size*/
-} ucvector;
-
-/*returns 1 if success, 0 if failure ==> nothing done*/
-static unsigned ucvector_reserve(ucvector* p, size_t size) {
- if(size > p->allocsize) {
- size_t newsize = size + (p->allocsize >> 1u);
- void* data = lodepng_realloc(p->data, newsize);
- if(data) {
- p->allocsize = newsize;
- p->data = (unsigned char*)data;
- }
- else return 0; /*error: not enough memory*/
- }
- return 1; /*success*/
-}
-
-/*returns 1 if success, 0 if failure ==> nothing done*/
-static unsigned ucvector_resize(ucvector* p, size_t size) {
- p->size = size;
- return ucvector_reserve(p, size);
-}
-
-static ucvector ucvector_init(unsigned char* buffer, size_t size) {
- ucvector v;
- v.data = buffer;
- v.allocsize = v.size = size;
- return v;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_PNG
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-
-/*free string pointer and set it to NULL*/
-static void string_cleanup(char** out) {
- lodepng_free(*out);
- *out = NULL;
-}
-
-/*also appends null termination character*/
-static char* alloc_string_sized(const char* in, size_t insize) {
- char* out = (char*)lodepng_malloc(insize + 1);
- if(out) {
- lodepng_memcpy(out, in, insize);
- out[insize] = 0;
- }
- return out;
-}
-
-/* dynamically allocates a new string with a copy of the null terminated input text */
-static char* alloc_string(const char* in) {
- return alloc_string_sized(in, lodepng_strlen(in));
-}
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-#endif /*LODEPNG_COMPILE_PNG*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_PNG)
-static unsigned lodepng_read32bitInt(const unsigned char* buffer) {
- return (((unsigned)buffer[0] << 24u) | ((unsigned)buffer[1] << 16u) |
- ((unsigned)buffer[2] << 8u) | (unsigned)buffer[3]);
-}
-#endif /*defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_PNG)*/
-
-#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)
-/*buffer must have at least 4 allocated bytes available*/
-static void lodepng_set32bitInt(unsigned char* buffer, unsigned value) {
- buffer[0] = (unsigned char)((value >> 24) & 0xff);
- buffer[1] = (unsigned char)((value >> 16) & 0xff);
- buffer[2] = (unsigned char)((value >> 8) & 0xff);
- buffer[3] = (unsigned char)((value ) & 0xff);
-}
-#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / File IO / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_DISK
-
-/* returns negative value on error. This should be pure C compatible, so no fstat. */
-static long lodepng_filesize(const char* filename) {
- FILE* file;
- long size;
- file = fopen(filename, "rb");
- if(!file) return -1;
-
- if(fseek(file, 0, SEEK_END) != 0) {
- fclose(file);
- return -1;
- }
-
- size = ftell(file);
- /* It may give LONG_MAX as directory size, this is invalid for us. */
- if(size == LONG_MAX) size = -1;
-
- fclose(file);
- return size;
-}
-
-/* load file into buffer that already has the correct allocated size. Returns error code.*/
-static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) {
- FILE* file;
- size_t readsize;
- file = fopen(filename, "rb");
- if(!file) return 78;
-
- readsize = fread(out, 1, size, file);
- fclose(file);
-
- if(readsize != size) return 78;
- return 0;
-}
-
-unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) {
- long size = lodepng_filesize(filename);
- if(size < 0) return 78;
- *outsize = (size_t)size;
-
- *out = (unsigned char*)lodepng_malloc((size_t)size);
- if(!(*out) && size > 0) return 83; /*the above malloc failed*/
-
- return lodepng_buffer_file(*out, (size_t)size, filename);
-}
-
-/*write given buffer to the file, overwriting the file, it doesn't append to it.*/
-unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename) {
- FILE* file;
- file = fopen(filename, "wb" );
- if(!file) return 79;
- fwrite(buffer, 1, buffersize, file);
- fclose(file);
- return 0;
-}
-
-#endif /*LODEPNG_COMPILE_DISK*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* // End of common code and tools. Begin of Zlib related code. // */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_ZLIB
-#ifdef LODEPNG_COMPILE_ENCODER
-
-typedef struct {
- ucvector* data;
- unsigned char bp; /*ok to overflow, indicates bit pos inside byte*/
-} LodePNGBitWriter;
-
-static void LodePNGBitWriter_init(LodePNGBitWriter* writer, ucvector* data) {
- writer->data = data;
- writer->bp = 0;
-}
-
-/*TODO: this ignores potential out of memory errors*/
-#define WRITEBIT(writer, bit){\
- /* append new byte */\
- if(((writer->bp) & 7u) == 0) {\
- if(!ucvector_resize(writer->data, writer->data->size + 1)) return;\
- writer->data->data[writer->data->size - 1] = 0;\
- }\
- (writer->data->data[writer->data->size - 1]) |= (bit << ((writer->bp) & 7u));\
- ++writer->bp;\
-}
-
-/* LSB of value is written first, and LSB of bytes is used first */
-static void writeBits(LodePNGBitWriter* writer, unsigned value, size_t nbits) {
- if(nbits == 1) { /* compiler should statically compile this case if nbits == 1 */
- WRITEBIT(writer, value);
- } else {
- /* TODO: increase output size only once here rather than in each WRITEBIT */
- size_t i;
- for(i = 0; i != nbits; ++i) {
- WRITEBIT(writer, (unsigned char)((value >> i) & 1));
- }
- }
-}
-
-/* This one is to use for adding huffman symbol, the value bits are written MSB first */
-static void writeBitsReversed(LodePNGBitWriter* writer, unsigned value, size_t nbits) {
- size_t i;
- for(i = 0; i != nbits; ++i) {
- /* TODO: increase output size only once here rather than in each WRITEBIT */
- WRITEBIT(writer, (unsigned char)((value >> (nbits - 1u - i)) & 1u));
- }
-}
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-typedef struct {
- const unsigned char* data;
- size_t size; /*size of data in bytes*/
- size_t bitsize; /*size of data in bits, end of valid bp values, should be 8*size*/
- size_t bp;
- unsigned buffer; /*buffer for reading bits. NOTE: 'unsigned' must support at least 32 bits*/
-} LodePNGBitReader;
-
-/* data size argument is in bytes. Returns error if size too large causing overflow */
-static unsigned LodePNGBitReader_init(LodePNGBitReader* reader, const unsigned char* data, size_t size) {
- size_t temp;
- reader->data = data;
- reader->size = size;
- /* size in bits, return error if overflow (if size_t is 32 bit this supports up to 500MB) */
- if(lodepng_mulofl(size, 8u, &reader->bitsize)) return 105;
- /*ensure incremented bp can be compared to bitsize without overflow even when it would be incremented 32 too much and
- trying to ensure 32 more bits*/
- if(lodepng_addofl(reader->bitsize, 64u, &temp)) return 105;
- reader->bp = 0;
- reader->buffer = 0;
- return 0; /*ok*/
-}
-
-/*
-ensureBits functions:
-Ensures the reader can at least read nbits bits in one or more readBits calls,
-safely even if not enough bits are available.
-The nbits parameter is unused but is given for documentation purposes, error
-checking for amount of bits must be done beforehand.
-*/
-
-/*See ensureBits documentation above. This one ensures up to 9 bits */
-static LODEPNG_INLINE void ensureBits9(LodePNGBitReader* reader, size_t nbits) {
- size_t start = reader->bp >> 3u;
- size_t size = reader->size;
- if(start + 1u < size) {
- reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u);
- reader->buffer >>= (reader->bp & 7u);
- } else {
- reader->buffer = 0;
- if(start + 0u < size) reader->buffer = reader->data[start + 0];
- reader->buffer >>= (reader->bp & 7u);
- }
- (void)nbits;
-}
-
-/*See ensureBits documentation above. This one ensures up to 17 bits */
-static LODEPNG_INLINE void ensureBits17(LodePNGBitReader* reader, size_t nbits) {
- size_t start = reader->bp >> 3u;
- size_t size = reader->size;
- if(start + 2u < size) {
- reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) |
- ((unsigned)reader->data[start + 2] << 16u);
- reader->buffer >>= (reader->bp & 7u);
- } else {
- reader->buffer = 0;
- if(start + 0u < size) reader->buffer |= reader->data[start + 0];
- if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
- reader->buffer >>= (reader->bp & 7u);
- }
- (void)nbits;
-}
-
-/*See ensureBits documentation above. This one ensures up to 25 bits */
-static LODEPNG_INLINE void ensureBits25(LodePNGBitReader* reader, size_t nbits) {
- size_t start = reader->bp >> 3u;
- size_t size = reader->size;
- if(start + 3u < size) {
- reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) |
- ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u);
- reader->buffer >>= (reader->bp & 7u);
- } else {
- reader->buffer = 0;
- if(start + 0u < size) reader->buffer |= reader->data[start + 0];
- if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
- if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
- reader->buffer >>= (reader->bp & 7u);
- }
- (void)nbits;
-}
-
-/*See ensureBits documentation above. This one ensures up to 32 bits */
-static LODEPNG_INLINE void ensureBits32(LodePNGBitReader* reader, size_t nbits) {
- size_t start = reader->bp >> 3u;
- size_t size = reader->size;
- if(start + 4u < size) {
- reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) |
- ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u);
- reader->buffer >>= (reader->bp & 7u);
- reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (8u - (reader->bp & 7u)));
- } else {
- reader->buffer = 0;
- if(start + 0u < size) reader->buffer |= reader->data[start + 0];
- if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
- if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
- if(start + 3u < size) reader->buffer |= ((unsigned)reader->data[start + 3] << 24u);
- reader->buffer >>= (reader->bp & 7u);
- }
- (void)nbits;
-}
-
-/* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits. Max nbits is 31. */
-static LODEPNG_INLINE unsigned peekBits(LodePNGBitReader* reader, size_t nbits) {
- /* The shift allows nbits to be only up to 31. */
- return reader->buffer & ((1u << nbits) - 1u);
-}
-
-/* Must have enough bits available with ensureBits */
-static LODEPNG_INLINE void advanceBits(LodePNGBitReader* reader, size_t nbits) {
- reader->buffer >>= nbits;
- reader->bp += nbits;
-}
-
-/* Must have enough bits available with ensureBits */
-static LODEPNG_INLINE unsigned readBits(LodePNGBitReader* reader, size_t nbits) {
- unsigned result = peekBits(reader, nbits);
- advanceBits(reader, nbits);
- return result;
-}
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-static unsigned reverseBits(unsigned bits, unsigned num) {
- /*TODO: implement faster lookup table based version when needed*/
- unsigned i, result = 0;
- for(i = 0; i < num; i++) result |= ((bits >> (num - i - 1u)) & 1u) << i;
- return result;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Deflate - Huffman / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#define FIRST_LENGTH_CODE_INDEX 257
-#define LAST_LENGTH_CODE_INDEX 285
-/*256 literals, the end code, some length codes, and 2 unused codes*/
-#define NUM_DEFLATE_CODE_SYMBOLS 288
-/*the distance codes have their own symbols, 30 used, 2 unused*/
-#define NUM_DISTANCE_SYMBOLS 32
-/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/
-#define NUM_CODE_LENGTH_CODES 19
-
-/*the base lengths represented by codes 257-285*/
-static const unsigned LENGTHBASE[29]
- = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
- 67, 83, 99, 115, 131, 163, 195, 227, 258};
-
-/*the extra bits used by codes 257-285 (added to base length)*/
-static const unsigned LENGTHEXTRA[29]
- = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
- 4, 4, 4, 4, 5, 5, 5, 5, 0};
-
-/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/
-static const unsigned DISTANCEBASE[30]
- = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
- 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577};
-
-/*the extra bits of backwards distances (added to base)*/
-static const unsigned DISTANCEEXTRA[30]
- = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8,
- 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13};
-
-/*the order in which "code length alphabet code lengths" are stored as specified by deflate, out of this the huffman
-tree of the dynamic huffman tree lengths is generated*/
-static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES]
- = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*
-Huffman tree struct, containing multiple representations of the tree
-*/
-typedef struct HuffmanTree {
- unsigned* codes; /*the huffman codes (bit patterns representing the symbols)*/
- unsigned* lengths; /*the lengths of the huffman codes*/
- unsigned maxbitlen; /*maximum number of bits a single code can get*/
- unsigned numcodes; /*number of symbols in the alphabet = number of codes*/
- /* for reading only */
- unsigned char* table_len; /*length of symbol from lookup table, or max length if secondary lookup needed*/
- unsigned short* table_value; /*value of symbol from lookup table, or pointer to secondary table if needed*/
-} HuffmanTree;
-
-static void HuffmanTree_init(HuffmanTree* tree) {
- tree->codes = 0;
- tree->lengths = 0;
- tree->table_len = 0;
- tree->table_value = 0;
-}
-
-static void HuffmanTree_cleanup(HuffmanTree* tree) {
- lodepng_free(tree->codes);
- lodepng_free(tree->lengths);
- lodepng_free(tree->table_len);
- lodepng_free(tree->table_value);
-}
-
-/* amount of bits for first huffman table lookup (aka root bits), see HuffmanTree_makeTable and huffmanDecodeSymbol.*/
-/* values 8u and 9u work the fastest */
-#define FIRSTBITS 9u
-
-/* a symbol value too big to represent any valid symbol, to indicate reading disallowed huffman bits combination,
-which is possible in case of only 0 or 1 present symbols. */
-#define INVALIDSYMBOL 65535u
-
-/* make table for huffman decoding */
-static unsigned HuffmanTree_makeTable(HuffmanTree* tree) {
- static const unsigned headsize = 1u << FIRSTBITS; /*size of the first table*/
- static const unsigned mask = (1u << FIRSTBITS) /*headsize*/ - 1u;
- size_t i, numpresent, pointer, size; /*total table size*/
- unsigned* maxlens = (unsigned*)lodepng_malloc(headsize * sizeof(unsigned));
- if(!maxlens) return 83; /*alloc fail*/
-
- /* compute maxlens: max total bit length of symbols sharing prefix in the first table*/
- lodepng_memset(maxlens, 0, headsize * sizeof(*maxlens));
- for(i = 0; i < tree->numcodes; i++) {
- unsigned symbol = tree->codes[i];
- unsigned l = tree->lengths[i];
- unsigned index;
- if(l <= FIRSTBITS) continue; /*symbols that fit in first table don't increase secondary table size*/
- /*get the FIRSTBITS MSBs, the MSBs of the symbol are encoded first. See later comment about the reversing*/
- index = reverseBits(symbol >> (l - FIRSTBITS), FIRSTBITS);
- maxlens[index] = LODEPNG_MAX(maxlens[index], l);
- }
- /* compute total table size: size of first table plus all secondary tables for symbols longer than FIRSTBITS */
- size = headsize;
- for(i = 0; i < headsize; ++i) {
- unsigned l = maxlens[i];
- if(l > FIRSTBITS) size += (1u << (l - FIRSTBITS));
- }
- tree->table_len = (unsigned char*)lodepng_malloc(size * sizeof(*tree->table_len));
- tree->table_value = (unsigned short*)lodepng_malloc(size * sizeof(*tree->table_value));
- if(!tree->table_len || !tree->table_value) {
- lodepng_free(maxlens);
- /* freeing tree->table values is done at a higher scope */
- return 83; /*alloc fail*/
- }
- /*initialize with an invalid length to indicate unused entries*/
- for(i = 0; i < size; ++i) tree->table_len[i] = 16;
-
- /*fill in the first table for long symbols: max prefix size and pointer to secondary tables*/
- pointer = headsize;
- for(i = 0; i < headsize; ++i) {
- unsigned l = maxlens[i];
- if(l <= FIRSTBITS) continue;
- tree->table_len[i] = l;
- tree->table_value[i] = pointer;
- pointer += (1u << (l - FIRSTBITS));
- }
- lodepng_free(maxlens);
-
- /*fill in the first table for short symbols, or secondary table for long symbols*/
- numpresent = 0;
- for(i = 0; i < tree->numcodes; ++i) {
- unsigned l = tree->lengths[i];
- unsigned symbol, reverse;
- if(l == 0) continue;
- symbol = tree->codes[i]; /*the huffman bit pattern. i itself is the value.*/
- /*reverse bits, because the huffman bits are given in MSB first order but the bit reader reads LSB first*/
- reverse = reverseBits(symbol, l);
- numpresent++;
-
- if(l <= FIRSTBITS) {
- /*short symbol, fully in first table, replicated num times if l < FIRSTBITS*/
- unsigned num = 1u << (FIRSTBITS - l);
- unsigned j;
- for(j = 0; j < num; ++j) {
- /*bit reader will read the l bits of symbol first, the remaining FIRSTBITS - l bits go to the MSB's*/
- unsigned index = reverse | (j << l);
- if(tree->table_len[index] != 16) return 55; /*invalid tree: long symbol shares prefix with short symbol*/
- tree->table_len[index] = l;
- tree->table_value[index] = i;
- }
- } else {
- /*long symbol, shares prefix with other long symbols in first lookup table, needs second lookup*/
- /*the FIRSTBITS MSBs of the symbol are the first table index*/
- unsigned index = reverse & mask;
- unsigned maxlen = tree->table_len[index];
- /*log2 of secondary table length, should be >= l - FIRSTBITS*/
- unsigned tablelen = maxlen - FIRSTBITS;
- unsigned start = tree->table_value[index]; /*starting index in secondary table*/
- unsigned num = 1u << (tablelen - (l - FIRSTBITS)); /*amount of entries of this symbol in secondary table*/
- unsigned j;
- if(maxlen < l) return 55; /*invalid tree: long symbol shares prefix with short symbol*/
- for(j = 0; j < num; ++j) {
- unsigned reverse2 = reverse >> FIRSTBITS; /* l - FIRSTBITS bits */
- unsigned index2 = start + (reverse2 | (j << (l - FIRSTBITS)));
- tree->table_len[index2] = l;
- tree->table_value[index2] = i;
- }
- }
- }
-
- if(numpresent < 2) {
- /* In case of exactly 1 symbol, in theory the huffman symbol needs 0 bits,
- but deflate uses 1 bit instead. In case of 0 symbols, no symbols can
- appear at all, but such huffman tree could still exist (e.g. if distance
- codes are never used). In both cases, not all symbols of the table will be
- filled in. Fill them in with an invalid symbol value so returning them from
- huffmanDecodeSymbol will cause error. */
- for(i = 0; i < size; ++i) {
- if(tree->table_len[i] == 16) {
- /* As length, use a value smaller than FIRSTBITS for the head table,
- and a value larger than FIRSTBITS for the secondary table, to ensure
- valid behavior for advanceBits when reading this symbol. */
- tree->table_len[i] = (i < headsize) ? 1 : (FIRSTBITS + 1);
- tree->table_value[i] = INVALIDSYMBOL;
- }
- }
- } else {
- /* A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes.
- If that is not the case (due to too long length codes), the table will not
- have been fully used, and this is an error (not all bit combinations can be
- decoded): an oversubscribed huffman tree, indicated by error 55. */
- for(i = 0; i < size; ++i) {
- if(tree->table_len[i] == 16) return 55;
- }
- }
-
- return 0;
-}
-
-/*
-Second step for the ...makeFromLengths and ...makeFromFrequencies functions.
-numcodes, lengths and maxbitlen must already be filled in correctly. return
-value is error.
-*/
-static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) {
- unsigned* blcount;
- unsigned* nextcode;
- unsigned error = 0;
- unsigned bits, n;
-
- tree->codes = (unsigned*)lodepng_malloc(tree->numcodes * sizeof(unsigned));
- blcount = (unsigned*)lodepng_malloc((tree->maxbitlen + 1) * sizeof(unsigned));
- nextcode = (unsigned*)lodepng_malloc((tree->maxbitlen + 1) * sizeof(unsigned));
- if(!tree->codes || !blcount || !nextcode) error = 83; /*alloc fail*/
-
- if(!error) {
- for(n = 0; n != tree->maxbitlen + 1; n++) blcount[n] = nextcode[n] = 0;
- /*step 1: count number of instances of each code length*/
- for(bits = 0; bits != tree->numcodes; ++bits) ++blcount[tree->lengths[bits]];
- /*step 2: generate the nextcode values*/
- for(bits = 1; bits <= tree->maxbitlen; ++bits) {
- nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1u;
- }
- /*step 3: generate all the codes*/
- for(n = 0; n != tree->numcodes; ++n) {
- if(tree->lengths[n] != 0) {
- tree->codes[n] = nextcode[tree->lengths[n]]++;
- /*remove superfluous bits from the code*/
- tree->codes[n] &= ((1u << tree->lengths[n]) - 1u);
- }
- }
- }
-
- lodepng_free(blcount);
- lodepng_free(nextcode);
-
- if(!error) error = HuffmanTree_makeTable(tree);
- return error;
-}
-
-/*
-given the code lengths (as stored in the PNG file), generate the tree as defined
-by Deflate. maxbitlen is the maximum bits that a code in the tree can have.
-return value is error.
-*/
-static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen,
- size_t numcodes, unsigned maxbitlen) {
- unsigned i;
- tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned));
- if(!tree->lengths) return 83; /*alloc fail*/
- for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i];
- tree->numcodes = (unsigned)numcodes; /*number of symbols*/
- tree->maxbitlen = maxbitlen;
- return HuffmanTree_makeFromLengths2(tree);
-}
-
-#ifdef LODEPNG_COMPILE_ENCODER
-
-/*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding",
-Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/
-
-/*chain node for boundary package merge*/
-typedef struct BPMNode {
- int weight; /*the sum of all weights in this chain*/
- unsigned index; /*index of this leaf node (called "count" in the paper)*/
- struct BPMNode* tail; /*the next nodes in this chain (null if last)*/
- int in_use;
-} BPMNode;
-
-/*lists of chains*/
-typedef struct BPMLists {
- /*memory pool*/
- unsigned memsize;
- BPMNode* memory;
- unsigned numfree;
- unsigned nextfree;
- BPMNode** freelist;
- /*two heads of lookahead chains per list*/
- unsigned listsize;
- BPMNode** chains0;
- BPMNode** chains1;
-} BPMLists;
-
-/*creates a new chain node with the given parameters, from the memory in the lists */
-static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) {
- unsigned i;
- BPMNode* result;
-
- /*memory full, so garbage collect*/
- if(lists->nextfree >= lists->numfree) {
- /*mark only those that are in use*/
- for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0;
- for(i = 0; i != lists->listsize; ++i) {
- BPMNode* node;
- for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1;
- for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1;
- }
- /*collect those that are free*/
- lists->numfree = 0;
- for(i = 0; i != lists->memsize; ++i) {
- if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i];
- }
- lists->nextfree = 0;
- }
-
- result = lists->freelist[lists->nextfree++];
- result->weight = weight;
- result->index = index;
- result->tail = tail;
- return result;
-}
-
-/*sort the leaves with stable mergesort*/
-static void bpmnode_sort(BPMNode* leaves, size_t num) {
- BPMNode* mem = (BPMNode*)lodepng_malloc(sizeof(*leaves) * num);
- size_t width, counter = 0;
- for(width = 1; width < num; width *= 2) {
- BPMNode* a = (counter & 1) ? mem : leaves;
- BPMNode* b = (counter & 1) ? leaves : mem;
- size_t p;
- for(p = 0; p < num; p += 2 * width) {
- size_t q = (p + width > num) ? num : (p + width);
- size_t r = (p + 2 * width > num) ? num : (p + 2 * width);
- size_t i = p, j = q, k;
- for(k = p; k < r; k++) {
- if(i < q && (j >= r || a[i].weight <= a[j].weight)) b[k] = a[i++];
- else b[k] = a[j++];
- }
- }
- counter++;
- }
- if(counter & 1) lodepng_memcpy(leaves, mem, sizeof(*leaves) * num);
- lodepng_free(mem);
-}
-
-/*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/
-static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) {
- unsigned lastindex = lists->chains1[c]->index;
-
- if(c == 0) {
- if(lastindex >= numpresent) return;
- lists->chains0[c] = lists->chains1[c];
- lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0);
- } else {
- /*sum of the weights of the head nodes of the previous lookahead chains.*/
- int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight;
- lists->chains0[c] = lists->chains1[c];
- if(lastindex < numpresent && sum > leaves[lastindex].weight) {
- lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail);
- return;
- }
- lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]);
- /*in the end we are only interested in the chain of the last list, so no
- need to recurse if we're at the last one (this gives measurable speedup)*/
- if(num + 1 < (int)(2 * numpresent - 2)) {
- boundaryPM(lists, leaves, numpresent, c - 1, num);
- boundaryPM(lists, leaves, numpresent, c - 1, num);
- }
- }
-}
-
-unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies,
- size_t numcodes, unsigned maxbitlen) {
- unsigned error = 0;
- unsigned i;
- size_t numpresent = 0; /*number of symbols with non-zero frequency*/
- BPMNode* leaves; /*the symbols, only those with > 0 frequency*/
-
- if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/
- if((1u << maxbitlen) < (unsigned)numcodes) return 80; /*error: represent all symbols*/
-
- leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves));
- if(!leaves) return 83; /*alloc fail*/
-
- for(i = 0; i != numcodes; ++i) {
- if(frequencies[i] > 0) {
- leaves[numpresent].weight = (int)frequencies[i];
- leaves[numpresent].index = i;
- ++numpresent;
- }
- }
-
- lodepng_memset(lengths, 0, numcodes * sizeof(*lengths));
-
- /*ensure at least two present symbols. There should be at least one symbol
- according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To
- make these work as well ensure there are at least two symbols. The
- Package-Merge code below also doesn't work correctly if there's only one
- symbol, it'd give it the theoretical 0 bits but in practice zlib wants 1 bit*/
- if(numpresent == 0) {
- lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/
- } else if(numpresent == 1) {
- lengths[leaves[0].index] = 1;
- lengths[leaves[0].index == 0 ? 1 : 0] = 1;
- } else {
- BPMLists lists;
- BPMNode* node;
-
- bpmnode_sort(leaves, numpresent);
-
- lists.listsize = maxbitlen;
- lists.memsize = 2 * maxbitlen * (maxbitlen + 1);
- lists.nextfree = 0;
- lists.numfree = lists.memsize;
- lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory));
- lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*));
- lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*));
- lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*));
- if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/
-
- if(!error) {
- for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i];
-
- bpmnode_create(&lists, leaves[0].weight, 1, 0);
- bpmnode_create(&lists, leaves[1].weight, 2, 0);
-
- for(i = 0; i != lists.listsize; ++i) {
- lists.chains0[i] = &lists.memory[0];
- lists.chains1[i] = &lists.memory[1];
- }
-
- /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/
- for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i);
-
- for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) {
- for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index];
- }
- }
-
- lodepng_free(lists.memory);
- lodepng_free(lists.freelist);
- lodepng_free(lists.chains0);
- lodepng_free(lists.chains1);
- }
-
- lodepng_free(leaves);
- return error;
-}
-
-/*Create the Huffman tree given the symbol frequencies*/
-static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies,
- size_t mincodes, size_t numcodes, unsigned maxbitlen) {
- unsigned error = 0;
- while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/
- tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned));
- if(!tree->lengths) return 83; /*alloc fail*/
- tree->maxbitlen = maxbitlen;
- tree->numcodes = (unsigned)numcodes; /*number of symbols*/
-
- error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen);
- if(!error) error = HuffmanTree_makeFromLengths2(tree);
- return error;
-}
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/
-static unsigned generateFixedLitLenTree(HuffmanTree* tree) {
- unsigned i, error = 0;
- unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned));
- if(!bitlen) return 83; /*alloc fail*/
-
- /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/
- for(i = 0; i <= 143; ++i) bitlen[i] = 8;
- for(i = 144; i <= 255; ++i) bitlen[i] = 9;
- for(i = 256; i <= 279; ++i) bitlen[i] = 7;
- for(i = 280; i <= 287; ++i) bitlen[i] = 8;
-
- error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15);
-
- lodepng_free(bitlen);
- return error;
-}
-
-/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/
-static unsigned generateFixedDistanceTree(HuffmanTree* tree) {
- unsigned i, error = 0;
- unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned));
- if(!bitlen) return 83; /*alloc fail*/
-
- /*there are 32 distance codes, but 30-31 are unused*/
- for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5;
- error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15);
-
- lodepng_free(bitlen);
- return error;
-}
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-/*
-returns the code. The bit reader must already have been ensured at least 15 bits
-*/
-static unsigned huffmanDecodeSymbol(LodePNGBitReader* reader, const HuffmanTree* codetree) {
- unsigned short code = peekBits(reader, FIRSTBITS);
- unsigned short l = codetree->table_len[code];
- unsigned short value = codetree->table_value[code];
- if(l <= FIRSTBITS) {
- advanceBits(reader, l);
- return value;
- } else {
- advanceBits(reader, FIRSTBITS);
- value += peekBits(reader, l - FIRSTBITS);
- advanceBits(reader, codetree->table_len[value] - FIRSTBITS);
- return codetree->table_value[value];
- }
-}
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Inflator (Decompressor) / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*get the tree of a deflated block with fixed tree, as specified in the deflate specification
-Returns error code.*/
-static unsigned getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) {
- unsigned error = generateFixedLitLenTree(tree_ll);
- if(error) return error;
- return generateFixedDistanceTree(tree_d);
-}
-
-/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/
-static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d,
- LodePNGBitReader* reader) {
- /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/
- unsigned error = 0;
- unsigned n, HLIT, HDIST, HCLEN, i;
-
- /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/
- unsigned* bitlen_ll = 0; /*lit,len code lengths*/
- unsigned* bitlen_d = 0; /*dist code lengths*/
- /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/
- unsigned* bitlen_cl = 0;
- HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/
-
- if(reader->bitsize - reader->bp < 14) return 49; /*error: the bit pointer is or will go past the memory*/
- ensureBits17(reader, 14);
-
- /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/
- HLIT = readBits(reader, 5) + 257;
- /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/
- HDIST = readBits(reader, 5) + 1;
- /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/
- HCLEN = readBits(reader, 4) + 4;
-
- bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned));
- if(!bitlen_cl) return 83 /*alloc fail*/;
-
- HuffmanTree_init(&tree_cl);
-
- while(!error) {
- /*read the code length codes out of 3 * (amount of code length codes) bits*/
- if(lodepng_gtofl(reader->bp, HCLEN * 3, reader->bitsize)) {
- ERROR_BREAK(50); /*error: the bit pointer is or will go past the memory*/
- }
- for(i = 0; i != HCLEN; ++i) {
- ensureBits9(reader, 3); /*out of bounds already checked above */
- bitlen_cl[CLCL_ORDER[i]] = readBits(reader, 3);
- }
- for(i = HCLEN; i != NUM_CODE_LENGTH_CODES; ++i) {
- bitlen_cl[CLCL_ORDER[i]] = 0;
- }
-
- error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7);
- if(error) break;
-
- /*now we can use this tree to read the lengths for the tree that this function will return*/
- bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned));
- bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned));
- if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/);
- lodepng_memset(bitlen_ll, 0, NUM_DEFLATE_CODE_SYMBOLS * sizeof(*bitlen_ll));
- lodepng_memset(bitlen_d, 0, NUM_DISTANCE_SYMBOLS * sizeof(*bitlen_d));
-
- /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/
- i = 0;
- while(i < HLIT + HDIST) {
- unsigned code;
- ensureBits25(reader, 22); /* up to 15 bits for huffman code, up to 7 extra bits below*/
- code = huffmanDecodeSymbol(reader, &tree_cl);
- if(code <= 15) /*a length code*/ {
- if(i < HLIT) bitlen_ll[i] = code;
- else bitlen_d[i - HLIT] = code;
- ++i;
- } else if(code == 16) /*repeat previous*/ {
- unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/
- unsigned value; /*set value to the previous code*/
-
- if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/
-
- replength += readBits(reader, 2);
-
- if(i < HLIT + 1) value = bitlen_ll[i - 1];
- else value = bitlen_d[i - HLIT - 1];
- /*repeat this value in the next lengths*/
- for(n = 0; n < replength; ++n) {
- if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/
- if(i < HLIT) bitlen_ll[i] = value;
- else bitlen_d[i - HLIT] = value;
- ++i;
- }
- } else if(code == 17) /*repeat "0" 3-10 times*/ {
- unsigned replength = 3; /*read in the bits that indicate repeat length*/
- replength += readBits(reader, 3);
-
- /*repeat this value in the next lengths*/
- for(n = 0; n < replength; ++n) {
- if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/
-
- if(i < HLIT) bitlen_ll[i] = 0;
- else bitlen_d[i - HLIT] = 0;
- ++i;
- }
- } else if(code == 18) /*repeat "0" 11-138 times*/ {
- unsigned replength = 11; /*read in the bits that indicate repeat length*/
- replength += readBits(reader, 7);
-
- /*repeat this value in the next lengths*/
- for(n = 0; n < replength; ++n) {
- if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/
-
- if(i < HLIT) bitlen_ll[i] = 0;
- else bitlen_d[i - HLIT] = 0;
- ++i;
- }
- } else /*if(code == INVALIDSYMBOL)*/ {
- ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
- }
- /*check if any of the ensureBits above went out of bounds*/
- if(reader->bp > reader->bitsize) {
- /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
- (10=no endcode, 11=wrong jump outside of tree)*/
- /* TODO: revise error codes 10,11,50: the above comment is no longer valid */
- ERROR_BREAK(50); /*error, bit pointer jumps past memory*/
- }
- }
- if(error) break;
-
- if(bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/
-
- /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/
- error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15);
- if(error) break;
- error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15);
-
- break; /*end of error-while*/
- }
-
- lodepng_free(bitlen_cl);
- lodepng_free(bitlen_ll);
- lodepng_free(bitlen_d);
- HuffmanTree_cleanup(&tree_cl);
-
- return error;
-}
-
-/*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/
-static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader,
- unsigned btype, size_t max_output_size) {
- unsigned error = 0;
- HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/
- HuffmanTree tree_d; /*the huffman tree for distance codes*/
- const size_t reserved_size = 260; /* must be at least 258 for max length, and a few extra for adding a few extra literals */
- int done = 0;
-
- if(!ucvector_reserve(out, out->size + reserved_size)) return 83; /*alloc fail*/
-
- HuffmanTree_init(&tree_ll);
- HuffmanTree_init(&tree_d);
-
- if(btype == 1) error = getTreeInflateFixed(&tree_ll, &tree_d);
- else /*if(btype == 2)*/ error = getTreeInflateDynamic(&tree_ll, &tree_d, reader);
-
-
- while(!error && !done) /*decode all symbols until end reached, breaks at end code*/ {
- /*code_ll is literal, length or end code*/
- unsigned code_ll;
- /* ensure enough bits for 2 huffman code reads (15 bits each): if the first is a literal, a second literal is read at once. This
- appears to be slightly faster, than ensuring 20 bits here for 1 huffman symbol and the potential 5 extra bits for the length symbol.*/
- ensureBits32(reader, 30);
- code_ll = huffmanDecodeSymbol(reader, &tree_ll);
- if(code_ll <= 255) {
- /*slightly faster code path if multiple literals in a row*/
- out->data[out->size++] = (unsigned char)code_ll;
- code_ll = huffmanDecodeSymbol(reader, &tree_ll);
- }
- if(code_ll <= 255) /*literal symbol*/ {
- out->data[out->size++] = (unsigned char)code_ll;
- } else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ {
- unsigned code_d, distance;
- unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/
- size_t start, backward, length;
-
- /*part 1: get length base*/
- length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX];
-
- /*part 2: get extra bits and add the value of that to length*/
- numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX];
- if(numextrabits_l != 0) {
- /* bits already ensured above */
- ensureBits25(reader, 5);
- length += readBits(reader, numextrabits_l);
- }
-
- /*part 3: get distance code*/
- ensureBits32(reader, 28); /* up to 15 for the huffman symbol, up to 13 for the extra bits */
- code_d = huffmanDecodeSymbol(reader, &tree_d);
- if(code_d > 29) {
- if(code_d <= 31) {
- ERROR_BREAK(18); /*error: invalid distance code (30-31 are never used)*/
- } else /* if(code_d == INVALIDSYMBOL) */{
- ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
- }
- }
- distance = DISTANCEBASE[code_d];
-
- /*part 4: get extra bits from distance*/
- numextrabits_d = DISTANCEEXTRA[code_d];
- if(numextrabits_d != 0) {
- /* bits already ensured above */
- distance += readBits(reader, numextrabits_d);
- }
-
- /*part 5: fill in all the out[n] values based on the length and dist*/
- start = out->size;
- if(distance > start) ERROR_BREAK(52); /*too long backward distance*/
- backward = start - distance;
-
- out->size += length;
- if(distance < length) {
- size_t forward;
- lodepng_memcpy(out->data + start, out->data + backward, distance);
- start += distance;
- for(forward = distance; forward < length; ++forward) {
- out->data[start++] = out->data[backward++];
- }
- } else {
- lodepng_memcpy(out->data + start, out->data + backward, length);
- }
- } else if(code_ll == 256) {
- done = 1; /*end code, finish the loop*/
- } else /*if(code_ll == INVALIDSYMBOL)*/ {
- ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
- }
- if(out->allocsize - out->size < reserved_size) {
- if(!ucvector_reserve(out, out->size + reserved_size)) ERROR_BREAK(83); /*alloc fail*/
- }
- /*check if any of the ensureBits above went out of bounds*/
- if(reader->bp > reader->bitsize) {
- /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
- (10=no endcode, 11=wrong jump outside of tree)*/
- /* TODO: revise error codes 10,11,50: the above comment is no longer valid */
- ERROR_BREAK(51); /*error, bit pointer jumps past memory*/
- }
- if(max_output_size && out->size > max_output_size) {
- ERROR_BREAK(109); /*error, larger than max size*/
- }
- }
-
- HuffmanTree_cleanup(&tree_ll);
- HuffmanTree_cleanup(&tree_d);
-
- return error;
-}
-
-static unsigned inflateNoCompression(ucvector* out, LodePNGBitReader* reader,
- const LodePNGDecompressSettings* settings) {
- size_t bytepos;
- size_t size = reader->size;
- unsigned LEN, NLEN, error = 0;
-
- /*go to first boundary of byte*/
- bytepos = (reader->bp + 7u) >> 3u;
-
- /*read LEN (2 bytes) and NLEN (2 bytes)*/
- if(bytepos + 4 >= size) return 52; /*error, bit pointer will jump past memory*/
- LEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2;
- NLEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2;
-
- /*check if 16-bit NLEN is really the one's complement of LEN*/
- if(!settings->ignore_nlen && LEN + NLEN != 65535) {
- return 21; /*error: NLEN is not one's complement of LEN*/
- }
-
- if(!ucvector_resize(out, out->size + LEN)) return 83; /*alloc fail*/
-
- /*read the literal data: LEN bytes are now stored in the out buffer*/
- if(bytepos + LEN > size) return 23; /*error: reading outside of in buffer*/
-
- /*out->data can be NULL (when LEN is zero), and arithmetics on NULL ptr is undefined*/
- if (LEN) {
- lodepng_memcpy(out->data + out->size - LEN, reader->data + bytepos, LEN);
- bytepos += LEN;
- }
-
- reader->bp = bytepos << 3u;
-
- return error;
-}
-
-static unsigned lodepng_inflatev(ucvector* out,
- const unsigned char* in, size_t insize,
- const LodePNGDecompressSettings* settings) {
- unsigned BFINAL = 0;
- LodePNGBitReader reader;
- unsigned error = LodePNGBitReader_init(&reader, in, insize);
-
- if(error) return error;
-
- while(!BFINAL) {
- unsigned BTYPE;
- if(reader.bitsize - reader.bp < 3) return 52; /*error, bit pointer will jump past memory*/
- ensureBits9(&reader, 3);
- BFINAL = readBits(&reader, 1);
- BTYPE = readBits(&reader, 2);
-
- if(BTYPE == 3) return 20; /*error: invalid BTYPE*/
- else if(BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/
- else error = inflateHuffmanBlock(out, &reader, BTYPE, settings->max_output_size); /*compression, BTYPE 01 or 10*/
- if(!error && settings->max_output_size && out->size > settings->max_output_size) error = 109;
- if(error) break;
- }
-
- return error;
-}
-
-unsigned lodepng_inflate(unsigned char** out, size_t* outsize,
- const unsigned char* in, size_t insize,
- const LodePNGDecompressSettings* settings) {
- ucvector v = ucvector_init(*out, *outsize);
- unsigned error = lodepng_inflatev(&v, in, insize, settings);
- *out = v.data;
- *outsize = v.size;
- return error;
-}
-
-static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize,
- const LodePNGDecompressSettings* settings) {
- if(settings->custom_inflate) {
- unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings);
- out->allocsize = out->size;
- if(error) {
- /*the custom inflate is allowed to have its own error codes, however, we translate it to code 110*/
- error = 110;
- /*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/
- if(settings->max_output_size && out->size > settings->max_output_size) error = 109;
- }
- return error;
- } else {
- return lodepng_inflatev(out, in, insize, settings);
- }
-}
-
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_ENCODER
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Deflator (Compressor) / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258;
-
-/*search the index in the array, that has the largest value smaller than or equal to the given value,
-given array must be sorted (if no value is smaller, it returns the size of the given array)*/
-static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) {
- /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/
- size_t left = 1;
- size_t right = array_size - 1;
-
- while(left <= right) {
- size_t mid = (left + right) >> 1;
- if(array[mid] >= value) right = mid - 1;
- else left = mid + 1;
- }
- if(left >= array_size || array[left] > value) left--;
- return left;
-}
-
-static void addLengthDistance(uivector* values, size_t length, size_t distance) {
- /*values in encoded vector are those used by deflate:
- 0-255: literal bytes
- 256: end
- 257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits)
- 286-287: invalid*/
-
- unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length);
- unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]);
- unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance);
- unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]);
-
- size_t pos = values->size;
- /*TODO: return error when this fails (out of memory)*/
- unsigned ok = uivector_resize(values, values->size + 4);
- if(ok) {
- values->data[pos + 0] = length_code + FIRST_LENGTH_CODE_INDEX;
- values->data[pos + 1] = extra_length;
- values->data[pos + 2] = dist_code;
- values->data[pos + 3] = extra_distance;
- }
-}
-
-/*3 bytes of data get encoded into two bytes. The hash cannot use more than 3
-bytes as input because 3 is the minimum match length for deflate*/
-static const unsigned HASH_NUM_VALUES = 65536;
-static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/
-
-typedef struct Hash {
- int* head; /*hash value to head circular pos - can be outdated if went around window*/
- /*circular pos to prev circular pos*/
- unsigned short* chain;
- int* val; /*circular pos to hash value*/
-
- /*TODO: do this not only for zeros but for any repeated byte. However for PNG
- it's always going to be the zeros that dominate, so not important for PNG*/
- int* headz; /*similar to head, but for chainz*/
- unsigned short* chainz; /*those with same amount of zeros*/
- unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/
-} Hash;
-
-static unsigned hash_init(Hash* hash, unsigned windowsize) {
- unsigned i;
- hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES);
- hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize);
- hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize);
-
- hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize);
- hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1));
- hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize);
-
- if(!hash->head || !hash->chain || !hash->val || !hash->headz|| !hash->chainz || !hash->zeros) {
- return 83; /*alloc fail*/
- }
-
- /*initialize hash table*/
- for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1;
- for(i = 0; i != windowsize; ++i) hash->val[i] = -1;
- for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/
-
- for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1;
- for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/
-
- return 0;
-}
-
-static void hash_cleanup(Hash* hash) {
- lodepng_free(hash->head);
- lodepng_free(hash->val);
- lodepng_free(hash->chain);
-
- lodepng_free(hash->zeros);
- lodepng_free(hash->headz);
- lodepng_free(hash->chainz);
-}
-
-
-
-static unsigned getHash(const unsigned char* data, size_t size, size_t pos) {
- unsigned result = 0;
- if(pos + 2 < size) {
- /*A simple shift and xor hash is used. Since the data of PNGs is dominated
- by zeroes due to the filters, a better hash does not have a significant
- effect on speed in traversing the chain, and causes more time spend on
- calculating the hash.*/
- result ^= ((unsigned)data[pos + 0] << 0u);
- result ^= ((unsigned)data[pos + 1] << 4u);
- result ^= ((unsigned)data[pos + 2] << 8u);
- } else {
- size_t amount, i;
- if(pos >= size) return 0;
- amount = size - pos;
- for(i = 0; i != amount; ++i) result ^= ((unsigned)data[pos + i] << (i * 8u));
- }
- return result & HASH_BIT_MASK;
-}
-
-static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) {
- const unsigned char* start = data + pos;
- const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH;
- if(end > data + size) end = data + size;
- data = start;
- while(data != end && *data == 0) ++data;
- /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/
- return (unsigned)(data - start);
-}
-
-/*wpos = pos & (windowsize - 1)*/
-static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) {
- hash->val[wpos] = (int)hashval;
- if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval];
- hash->head[hashval] = (int)wpos;
-
- hash->zeros[wpos] = numzeros;
- if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros];
- hash->headz[numzeros] = (int)wpos;
-}
-
-/*
-LZ77-encode the data. Return value is error code. The input are raw bytes, the output
-is in the form of unsigned integers with codes representing for example literal bytes, or
-length/distance pairs.
-It uses a hash table technique to let it encode faster. When doing LZ77 encoding, a
-sliding window (of windowsize) is used, and all past bytes in that window can be used as
-the "dictionary". A brute force search through all possible distances would be slow, and
-this hash technique is one out of several ways to speed this up.
-*/
-static unsigned encodeLZ77(uivector* out, Hash* hash,
- const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize,
- unsigned minmatch, unsigned nicematch, unsigned lazymatching) {
- size_t pos;
- unsigned i, error = 0;
- /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/
- unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8u;
- unsigned maxlazymatch = windowsize >= 8192 ? MAX_SUPPORTED_DEFLATE_LENGTH : 64;
-
- unsigned usezeros = 1; /*not sure if setting it to false for windowsize < 8192 is better or worse*/
- unsigned numzeros = 0;
-
- unsigned offset; /*the offset represents the distance in LZ77 terminology*/
- unsigned length;
- unsigned lazy = 0;
- unsigned lazylength = 0, lazyoffset = 0;
- unsigned hashval;
- unsigned current_offset, current_length;
- unsigned prev_offset;
- const unsigned char *lastptr, *foreptr, *backptr;
- unsigned hashpos;
-
- if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/
- if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/
-
- if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH;
-
- for(pos = inpos; pos < insize; ++pos) {
- size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/
- unsigned chainlength = 0;
-
- hashval = getHash(in, insize, pos);
-
- if(usezeros && hashval == 0) {
- if(numzeros == 0) numzeros = countZeros(in, insize, pos);
- else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros;
- } else {
- numzeros = 0;
- }
-
- updateHashChain(hash, wpos, hashval, numzeros);
-
- /*the length and offset found for the current position*/
- length = 0;
- offset = 0;
-
- hashpos = hash->chain[wpos];
-
- lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH];
-
- /*search for the longest string*/
- prev_offset = 0;
- for(;;) {
- if(chainlength++ >= maxchainlength) break;
- current_offset = (unsigned)(hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize);
-
- if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/
- prev_offset = current_offset;
- if(current_offset > 0) {
- /*test the next characters*/
- foreptr = &in[pos];
- backptr = &in[pos - current_offset];
-
- /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/
- if(numzeros >= 3) {
- unsigned skip = hash->zeros[hashpos];
- if(skip > numzeros) skip = numzeros;
- backptr += skip;
- foreptr += skip;
- }
-
- while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/ {
- ++backptr;
- ++foreptr;
- }
- current_length = (unsigned)(foreptr - &in[pos]);
-
- if(current_length > length) {
- length = current_length; /*the longest length*/
- offset = current_offset; /*the offset that is related to this longest length*/
- /*jump out once a length of max length is found (speed gain). This also jumps
- out if length is MAX_SUPPORTED_DEFLATE_LENGTH*/
- if(current_length >= nicematch) break;
- }
- }
-
- if(hashpos == hash->chain[hashpos]) break;
-
- if(numzeros >= 3 && length > numzeros) {
- hashpos = hash->chainz[hashpos];
- if(hash->zeros[hashpos] != numzeros) break;
- } else {
- hashpos = hash->chain[hashpos];
- /*outdated hash value, happens if particular value was not encountered in whole last window*/
- if(hash->val[hashpos] != (int)hashval) break;
- }
- }
-
- if(lazymatching) {
- if(!lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH) {
- lazy = 1;
- lazylength = length;
- lazyoffset = offset;
- continue; /*try the next byte*/
- }
- if(lazy) {
- lazy = 0;
- if(pos == 0) ERROR_BREAK(81);
- if(length > lazylength + 1) {
- /*push the previous character as literal*/
- if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/);
- } else {
- length = lazylength;
- offset = lazyoffset;
- hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/
- hash->headz[numzeros] = -1; /*idem*/
- --pos;
- }
- }
- }
- if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/);
-
- /*encode it as length/distance pair or literal value*/
- if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/ {
- if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/);
- } else if(length < minmatch || (length == 3 && offset > 4096)) {
- /*compensate for the fact that longer offsets have more extra bits, a
- length of only 3 may be not worth it then*/
- if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/);
- } else {
- addLengthDistance(out, length, offset);
- for(i = 1; i < length; ++i) {
- ++pos;
- wpos = pos & (windowsize - 1);
- hashval = getHash(in, insize, pos);
- if(usezeros && hashval == 0) {
- if(numzeros == 0) numzeros = countZeros(in, insize, pos);
- else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros;
- } else {
- numzeros = 0;
- }
- updateHashChain(hash, wpos, hashval, numzeros);
- }
- }
- } /*end of the loop through each character of input*/
-
- return error;
-}
-
-/* /////////////////////////////////////////////////////////////////////////// */
-
-static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize) {
- /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte,
- 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/
-
- size_t i, numdeflateblocks = (datasize + 65534u) / 65535u;
- unsigned datapos = 0;
- for(i = 0; i != numdeflateblocks; ++i) {
- unsigned BFINAL, BTYPE, LEN, NLEN;
- unsigned char firstbyte;
- size_t pos = out->size;
-
- BFINAL = (i == numdeflateblocks - 1);
- BTYPE = 0;
-
- LEN = 65535;
- if(datasize - datapos < 65535u) LEN = (unsigned)datasize - datapos;
- NLEN = 65535 - LEN;
-
- if(!ucvector_resize(out, out->size + LEN + 5)) return 83; /*alloc fail*/
-
- firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1u) << 1u) + ((BTYPE & 2u) << 1u));
- out->data[pos + 0] = firstbyte;
- out->data[pos + 1] = (unsigned char)(LEN & 255);
- out->data[pos + 2] = (unsigned char)(LEN >> 8u);
- out->data[pos + 3] = (unsigned char)(NLEN & 255);
- out->data[pos + 4] = (unsigned char)(NLEN >> 8u);
- lodepng_memcpy(out->data + pos + 5, data + datapos, LEN);
- datapos += LEN;
- }
-
- return 0;
-}
-
-/*
-write the lz77-encoded data, which has lit, len and dist codes, to compressed stream using huffman trees.
-tree_ll: the tree for lit and len codes.
-tree_d: the tree for distance codes.
-*/
-static void writeLZ77data(LodePNGBitWriter* writer, const uivector* lz77_encoded,
- const HuffmanTree* tree_ll, const HuffmanTree* tree_d) {
- size_t i = 0;
- for(i = 0; i != lz77_encoded->size; ++i) {
- unsigned val = lz77_encoded->data[i];
- writeBitsReversed(writer, tree_ll->codes[val], tree_ll->lengths[val]);
- if(val > 256) /*for a length code, 3 more things have to be added*/ {
- unsigned length_index = val - FIRST_LENGTH_CODE_INDEX;
- unsigned n_length_extra_bits = LENGTHEXTRA[length_index];
- unsigned length_extra_bits = lz77_encoded->data[++i];
-
- unsigned distance_code = lz77_encoded->data[++i];
-
- unsigned distance_index = distance_code;
- unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index];
- unsigned distance_extra_bits = lz77_encoded->data[++i];
-
- writeBits(writer, length_extra_bits, n_length_extra_bits);
- writeBitsReversed(writer, tree_d->codes[distance_code], tree_d->lengths[distance_code]);
- writeBits(writer, distance_extra_bits, n_distance_extra_bits);
- }
- }
-}
-
-/*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/
-static unsigned deflateDynamic(LodePNGBitWriter* writer, Hash* hash,
- const unsigned char* data, size_t datapos, size_t dataend,
- const LodePNGCompressSettings* settings, unsigned final) {
- unsigned error = 0;
-
- /*
- A block is compressed as follows: The PNG data is lz77 encoded, resulting in
- literal bytes and length/distance pairs. This is then huffman compressed with
- two huffman trees. One huffman tree is used for the lit and len values ("ll"),
- another huffman tree is used for the dist values ("d"). These two trees are
- stored using their code lengths, and to compress even more these code lengths
- are also run-length encoded and huffman compressed. This gives a huffman tree
- of code lengths "cl". The code lengths used to describe this third tree are
- the code length code lengths ("clcl").
- */
-
- /*The lz77 encoded data, represented with integers since there will also be length and distance codes in it*/
- uivector lz77_encoded;
- HuffmanTree tree_ll; /*tree for lit,len values*/
- HuffmanTree tree_d; /*tree for distance codes*/
- HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/
- unsigned* frequencies_ll = 0; /*frequency of lit,len codes*/
- unsigned* frequencies_d = 0; /*frequency of dist codes*/
- unsigned* frequencies_cl = 0; /*frequency of code length codes*/
- unsigned* bitlen_lld = 0; /*lit,len,dist code lengths (int bits), literally (without repeat codes).*/
- unsigned* bitlen_lld_e = 0; /*bitlen_lld encoded with repeat codes (this is a rudimentary run length compression)*/
- size_t datasize = dataend - datapos;
-
- /*
- If we could call "bitlen_cl" the the code length code lengths ("clcl"), that is the bit lengths of codes to represent
- tree_cl in CLCL_ORDER, then due to the huffman compression of huffman tree representations ("two levels"), there are
- some analogies:
- bitlen_lld is to tree_cl what data is to tree_ll and tree_d.
- bitlen_lld_e is to bitlen_lld what lz77_encoded is to data.
- bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded.
- */
-
- unsigned BFINAL = final;
- size_t i;
- size_t numcodes_ll, numcodes_d, numcodes_lld, numcodes_lld_e, numcodes_cl;
- unsigned HLIT, HDIST, HCLEN;
-
- uivector_init(&lz77_encoded);
- HuffmanTree_init(&tree_ll);
- HuffmanTree_init(&tree_d);
- HuffmanTree_init(&tree_cl);
- /* could fit on stack, but >1KB is on the larger side so allocate instead */
- frequencies_ll = (unsigned*)lodepng_malloc(286 * sizeof(*frequencies_ll));
- frequencies_d = (unsigned*)lodepng_malloc(30 * sizeof(*frequencies_d));
- frequencies_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(*frequencies_cl));
-
- if(!frequencies_ll || !frequencies_d || !frequencies_cl) error = 83; /*alloc fail*/
-
- /*This while loop never loops due to a break at the end, it is here to
- allow breaking out of it to the cleanup phase on error conditions.*/
- while(!error) {
- lodepng_memset(frequencies_ll, 0, 286 * sizeof(*frequencies_ll));
- lodepng_memset(frequencies_d, 0, 30 * sizeof(*frequencies_d));
- lodepng_memset(frequencies_cl, 0, NUM_CODE_LENGTH_CODES * sizeof(*frequencies_cl));
-
- if(settings->use_lz77) {
- error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize,
- settings->minmatch, settings->nicematch, settings->lazymatching);
- if(error) break;
- } else {
- if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/);
- for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/
- }
-
- /*Count the frequencies of lit, len and dist codes*/
- for(i = 0; i != lz77_encoded.size; ++i) {
- unsigned symbol = lz77_encoded.data[i];
- ++frequencies_ll[symbol];
- if(symbol > 256) {
- unsigned dist = lz77_encoded.data[i + 2];
- ++frequencies_d[dist];
- i += 3;
- }
- }
- frequencies_ll[256] = 1; /*there will be exactly 1 end code, at the end of the block*/
-
- /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/
- error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll, 257, 286, 15);
- if(error) break;
- /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/
- error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d, 2, 30, 15);
- if(error) break;
-
- numcodes_ll = LODEPNG_MIN(tree_ll.numcodes, 286);
- numcodes_d = LODEPNG_MIN(tree_d.numcodes, 30);
- /*store the code lengths of both generated trees in bitlen_lld*/
- numcodes_lld = numcodes_ll + numcodes_d;
- bitlen_lld = (unsigned*)lodepng_malloc(numcodes_lld * sizeof(*bitlen_lld));
- /*numcodes_lld_e never needs more size than bitlen_lld*/
- bitlen_lld_e = (unsigned*)lodepng_malloc(numcodes_lld * sizeof(*bitlen_lld_e));
- if(!bitlen_lld || !bitlen_lld_e) ERROR_BREAK(83); /*alloc fail*/
- numcodes_lld_e = 0;
-
- for(i = 0; i != numcodes_ll; ++i) bitlen_lld[i] = tree_ll.lengths[i];
- for(i = 0; i != numcodes_d; ++i) bitlen_lld[numcodes_ll + i] = tree_d.lengths[i];
-
- /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times),
- 17 (3-10 zeroes), 18 (11-138 zeroes)*/
- for(i = 0; i != numcodes_lld; ++i) {
- unsigned j = 0; /*amount of repetitions*/
- while(i + j + 1 < numcodes_lld && bitlen_lld[i + j + 1] == bitlen_lld[i]) ++j;
-
- if(bitlen_lld[i] == 0 && j >= 2) /*repeat code for zeroes*/ {
- ++j; /*include the first zero*/
- if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ {
- bitlen_lld_e[numcodes_lld_e++] = 17;
- bitlen_lld_e[numcodes_lld_e++] = j - 3;
- } else /*repeat code 18 supports max 138 zeroes*/ {
- if(j > 138) j = 138;
- bitlen_lld_e[numcodes_lld_e++] = 18;
- bitlen_lld_e[numcodes_lld_e++] = j - 11;
- }
- i += (j - 1);
- } else if(j >= 3) /*repeat code for value other than zero*/ {
- size_t k;
- unsigned num = j / 6u, rest = j % 6u;
- bitlen_lld_e[numcodes_lld_e++] = bitlen_lld[i];
- for(k = 0; k < num; ++k) {
- bitlen_lld_e[numcodes_lld_e++] = 16;
- bitlen_lld_e[numcodes_lld_e++] = 6 - 3;
- }
- if(rest >= 3) {
- bitlen_lld_e[numcodes_lld_e++] = 16;
- bitlen_lld_e[numcodes_lld_e++] = rest - 3;
- }
- else j -= rest;
- i += j;
- } else /*too short to benefit from repeat code*/ {
- bitlen_lld_e[numcodes_lld_e++] = bitlen_lld[i];
- }
- }
-
- /*generate tree_cl, the huffmantree of huffmantrees*/
- for(i = 0; i != numcodes_lld_e; ++i) {
- ++frequencies_cl[bitlen_lld_e[i]];
- /*after a repeat code come the bits that specify the number of repetitions,
- those don't need to be in the frequencies_cl calculation*/
- if(bitlen_lld_e[i] >= 16) ++i;
- }
-
- error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl,
- NUM_CODE_LENGTH_CODES, NUM_CODE_LENGTH_CODES, 7);
- if(error) break;
-
- /*compute amount of code-length-code-lengths to output*/
- numcodes_cl = NUM_CODE_LENGTH_CODES;
- /*trim zeros at the end (using CLCL_ORDER), but minimum size must be 4 (see HCLEN below)*/
- while(numcodes_cl > 4u && tree_cl.lengths[CLCL_ORDER[numcodes_cl - 1u]] == 0) {
- numcodes_cl--;
- }
-
- /*
- Write everything into the output
-
- After the BFINAL and BTYPE, the dynamic block consists out of the following:
- - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN
- - (HCLEN+4)*3 bits code lengths of code length alphabet
- - HLIT + 257 code lengths of lit/length alphabet (encoded using the code length
- alphabet, + possible repetition codes 16, 17, 18)
- - HDIST + 1 code lengths of distance alphabet (encoded using the code length
- alphabet, + possible repetition codes 16, 17, 18)
- - compressed data
- - 256 (end code)
- */
-
- /*Write block type*/
- writeBits(writer, BFINAL, 1);
- writeBits(writer, 0, 1); /*first bit of BTYPE "dynamic"*/
- writeBits(writer, 1, 1); /*second bit of BTYPE "dynamic"*/
-
- /*write the HLIT, HDIST and HCLEN values*/
- /*all three sizes take trimmed ending zeroes into account, done either by HuffmanTree_makeFromFrequencies
- or in the loop for numcodes_cl above, which saves space. */
- HLIT = (unsigned)(numcodes_ll - 257);
- HDIST = (unsigned)(numcodes_d - 1);
- HCLEN = (unsigned)(numcodes_cl - 4);
- writeBits(writer, HLIT, 5);
- writeBits(writer, HDIST, 5);
- writeBits(writer, HCLEN, 4);
-
- /*write the code lengths of the code length alphabet ("bitlen_cl")*/
- for(i = 0; i != numcodes_cl; ++i) writeBits(writer, tree_cl.lengths[CLCL_ORDER[i]], 3);
-
- /*write the lengths of the lit/len AND the dist alphabet*/
- for(i = 0; i != numcodes_lld_e; ++i) {
- writeBitsReversed(writer, tree_cl.codes[bitlen_lld_e[i]], tree_cl.lengths[bitlen_lld_e[i]]);
- /*extra bits of repeat codes*/
- if(bitlen_lld_e[i] == 16) writeBits(writer, bitlen_lld_e[++i], 2);
- else if(bitlen_lld_e[i] == 17) writeBits(writer, bitlen_lld_e[++i], 3);
- else if(bitlen_lld_e[i] == 18) writeBits(writer, bitlen_lld_e[++i], 7);
- }
-
- /*write the compressed data symbols*/
- writeLZ77data(writer, &lz77_encoded, &tree_ll, &tree_d);
- /*error: the length of the end code 256 must be larger than 0*/
- if(tree_ll.lengths[256] == 0) ERROR_BREAK(64);
-
- /*write the end code*/
- writeBitsReversed(writer, tree_ll.codes[256], tree_ll.lengths[256]);
-
- break; /*end of error-while*/
- }
-
- /*cleanup*/
- uivector_cleanup(&lz77_encoded);
- HuffmanTree_cleanup(&tree_ll);
- HuffmanTree_cleanup(&tree_d);
- HuffmanTree_cleanup(&tree_cl);
- lodepng_free(frequencies_ll);
- lodepng_free(frequencies_d);
- lodepng_free(frequencies_cl);
- lodepng_free(bitlen_lld);
- lodepng_free(bitlen_lld_e);
-
- return error;
-}
-
-static unsigned deflateFixed(LodePNGBitWriter* writer, Hash* hash,
- const unsigned char* data,
- size_t datapos, size_t dataend,
- const LodePNGCompressSettings* settings, unsigned final) {
- HuffmanTree tree_ll; /*tree for literal values and length codes*/
- HuffmanTree tree_d; /*tree for distance codes*/
-
- unsigned BFINAL = final;
- unsigned error = 0;
- size_t i;
-
- HuffmanTree_init(&tree_ll);
- HuffmanTree_init(&tree_d);
-
- error = generateFixedLitLenTree(&tree_ll);
- if(!error) error = generateFixedDistanceTree(&tree_d);
-
- if(!error) {
- writeBits(writer, BFINAL, 1);
- writeBits(writer, 1, 1); /*first bit of BTYPE*/
- writeBits(writer, 0, 1); /*second bit of BTYPE*/
-
- if(settings->use_lz77) /*LZ77 encoded*/ {
- uivector lz77_encoded;
- uivector_init(&lz77_encoded);
- error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize,
- settings->minmatch, settings->nicematch, settings->lazymatching);
- if(!error) writeLZ77data(writer, &lz77_encoded, &tree_ll, &tree_d);
- uivector_cleanup(&lz77_encoded);
- } else /*no LZ77, but still will be Huffman compressed*/ {
- for(i = datapos; i < dataend; ++i) {
- writeBitsReversed(writer, tree_ll.codes[data[i]], tree_ll.lengths[data[i]]);
- }
- }
- /*add END code*/
- if(!error) writeBitsReversed(writer,tree_ll.codes[256], tree_ll.lengths[256]);
- }
-
- /*cleanup*/
- HuffmanTree_cleanup(&tree_ll);
- HuffmanTree_cleanup(&tree_d);
-
- return error;
-}
-
-static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t insize,
- const LodePNGCompressSettings* settings) {
- unsigned error = 0;
- size_t i, blocksize, numdeflateblocks;
- Hash hash;
- LodePNGBitWriter writer;
-
- LodePNGBitWriter_init(&writer, out);
-
- if(settings->btype > 2) return 61;
- else if(settings->btype == 0) return deflateNoCompression(out, in, insize);
- else if(settings->btype == 1) blocksize = insize;
- else /*if(settings->btype == 2)*/ {
- /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/
- blocksize = insize / 8u + 8;
- if(blocksize < 65536) blocksize = 65536;
- if(blocksize > 262144) blocksize = 262144;
- }
-
- numdeflateblocks = (insize + blocksize - 1) / blocksize;
- if(numdeflateblocks == 0) numdeflateblocks = 1;
-
- error = hash_init(&hash, settings->windowsize);
-
- if(!error) {
- for(i = 0; i != numdeflateblocks && !error; ++i) {
- unsigned final = (i == numdeflateblocks - 1);
- size_t start = i * blocksize;
- size_t end = start + blocksize;
- if(end > insize) end = insize;
-
- if(settings->btype == 1) error = deflateFixed(&writer, &hash, in, start, end, settings, final);
- else if(settings->btype == 2) error = deflateDynamic(&writer, &hash, in, start, end, settings, final);
- }
- }
-
- hash_cleanup(&hash);
-
- return error;
-}
-
-unsigned lodepng_deflate(unsigned char** out, size_t* outsize,
- const unsigned char* in, size_t insize,
- const LodePNGCompressSettings* settings) {
- ucvector v = ucvector_init(*out, *outsize);
- unsigned error = lodepng_deflatev(&v, in, insize, settings);
- *out = v.data;
- *outsize = v.size;
- return error;
-}
-
-static unsigned deflate(unsigned char** out, size_t* outsize,
- const unsigned char* in, size_t insize,
- const LodePNGCompressSettings* settings) {
- if(settings->custom_deflate) {
- unsigned error = settings->custom_deflate(out, outsize, in, insize, settings);
- /*the custom deflate is allowed to have its own error codes, however, we translate it to code 111*/
- return error ? 111 : 0;
- } else {
- return lodepng_deflate(out, outsize, in, insize, settings);
- }
-}
-
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Adler32 / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) {
- unsigned s1 = adler & 0xffffu;
- unsigned s2 = (adler >> 16u) & 0xffffu;
-
- while(len != 0u) {
- unsigned i;
- /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/
- unsigned amount = len > 5552u ? 5552u : len;
- len -= amount;
- for(i = 0; i != amount; ++i) {
- s1 += (*data++);
- s2 += s1;
- }
- s1 %= 65521u;
- s2 %= 65521u;
- }
-
- return (s2 << 16u) | s1;
-}
-
-/*Return the adler32 of the bytes data[0..len-1]*/
-static unsigned adler32(const unsigned char* data, unsigned len) {
- return update_adler32(1u, data, len);
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Zlib / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-static unsigned lodepng_zlib_decompressv(ucvector* out,
- const unsigned char* in, size_t insize,
- const LodePNGDecompressSettings* settings) {
- unsigned error = 0;
- unsigned CM, CINFO, FDICT;
-
- if(insize < 2) return 53; /*error, size of zlib data too small*/
- /*read information from zlib header*/
- if((in[0] * 256 + in[1]) % 31 != 0) {
- /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/
- return 24;
- }
-
- CM = in[0] & 15;
- CINFO = (in[0] >> 4) & 15;
- /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/
- FDICT = (in[1] >> 5) & 1;
- /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/
-
- if(CM != 8 || CINFO > 7) {
- /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/
- return 25;
- }
- if(FDICT != 0) {
- /*error: the specification of PNG says about the zlib stream:
- "The additional flags shall not specify a preset dictionary."*/
- return 26;
- }
-
- error = inflatev(out, in + 2, insize - 2, settings);
- if(error) return error;
-
- if(!settings->ignore_adler32) {
- unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]);
- unsigned checksum = adler32(out->data, (unsigned)(out->size));
- if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/
- }
-
- return 0; /*no error*/
-}
-
-
-unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in,
- size_t insize, const LodePNGDecompressSettings* settings) {
- ucvector v = ucvector_init(*out, *outsize);
- unsigned error = lodepng_zlib_decompressv(&v, in, insize, settings);
- *out = v.data;
- *outsize = v.size;
- return error;
-}
-
-/*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */
-static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size,
- const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) {
- unsigned error;
- if(settings->custom_zlib) {
- error = settings->custom_zlib(out, outsize, in, insize, settings);
- if(error) {
- /*the custom zlib is allowed to have its own error codes, however, we translate it to code 110*/
- error = 110;
- /*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/
- if(settings->max_output_size && *outsize > settings->max_output_size) error = 109;
- }
- } else {
- ucvector v = ucvector_init(*out, *outsize);
- if(expected_size) {
- /*reserve the memory to avoid intermediate reallocations*/
- ucvector_resize(&v, *outsize + expected_size);
- v.size = *outsize;
- }
- error = lodepng_zlib_decompressv(&v, in, insize, settings);
- *out = v.data;
- *outsize = v.size;
- }
- return error;
-}
-
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_ENCODER
-
-unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
- size_t insize, const LodePNGCompressSettings* settings) {
- size_t i;
- unsigned error;
- unsigned char* deflatedata = 0;
- size_t deflatesize = 0;
-
- error = deflate(&deflatedata, &deflatesize, in, insize, settings);
-
- *out = NULL;
- *outsize = 0;
- if(!error) {
- *outsize = deflatesize + 6;
- *out = (unsigned char*)lodepng_malloc(*outsize);
- if(!*out) error = 83; /*alloc fail*/
- }
-
- if(!error) {
- unsigned ADLER32 = adler32(in, (unsigned)insize);
- /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/
- unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/
- unsigned FLEVEL = 0;
- unsigned FDICT = 0;
- unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64;
- unsigned FCHECK = 31 - CMFFLG % 31;
- CMFFLG += FCHECK;
-
- (*out)[0] = (unsigned char)(CMFFLG >> 8);
- (*out)[1] = (unsigned char)(CMFFLG & 255);
- for(i = 0; i != deflatesize; ++i) (*out)[i + 2] = deflatedata[i];
- lodepng_set32bitInt(&(*out)[*outsize - 4], ADLER32);
- }
-
- lodepng_free(deflatedata);
- return error;
-}
-
-/* compress using the default or custom zlib function */
-static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
- size_t insize, const LodePNGCompressSettings* settings) {
- if(settings->custom_zlib) {
- unsigned error = settings->custom_zlib(out, outsize, in, insize, settings);
- /*the custom zlib is allowed to have its own error codes, however, we translate it to code 111*/
- return error ? 111 : 0;
- } else {
- return lodepng_zlib_compress(out, outsize, in, insize, settings);
- }
-}
-
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-#else /*no LODEPNG_COMPILE_ZLIB*/
-
-#ifdef LODEPNG_COMPILE_DECODER
-static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size,
- const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) {
- if(!settings->custom_zlib) return 87; /*no custom zlib function provided */
- (void)expected_size;
- return settings->custom_zlib(out, outsize, in, insize, settings);
-}
-#endif /*LODEPNG_COMPILE_DECODER*/
-#ifdef LODEPNG_COMPILE_ENCODER
-static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
- size_t insize, const LodePNGCompressSettings* settings) {
- if(!settings->custom_zlib) return 87; /*no custom zlib function provided */
- return settings->custom_zlib(out, outsize, in, insize, settings);
-}
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-#endif /*LODEPNG_COMPILE_ZLIB*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_ENCODER
-
-/*this is a good tradeoff between speed and compression ratio*/
-#define DEFAULT_WINDOWSIZE 2048
-
-void lodepng_compress_settings_init(LodePNGCompressSettings* settings) {
- /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/
- settings->btype = 2;
- settings->use_lz77 = 1;
- settings->windowsize = DEFAULT_WINDOWSIZE;
- settings->minmatch = 3;
- settings->nicematch = 128;
- settings->lazymatching = 1;
-
- settings->custom_zlib = 0;
- settings->custom_deflate = 0;
- settings->custom_context = 0;
-}
-
-const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT_WINDOWSIZE, 3, 128, 1, 0, 0, 0};
-
-
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) {
- settings->ignore_adler32 = 0;
- settings->ignore_nlen = 0;
- settings->max_output_size = 0;
-
- settings->custom_zlib = 0;
- settings->custom_inflate = 0;
- settings->custom_context = 0;
-}
-
-const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0, 0};
-
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* // End of Zlib related code. Begin of PNG related code. // */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_PNG
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / CRC32 / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-
-#ifdef LODEPNG_COMPILE_CRC
-
-static const unsigned lodepng_crc32_table0[256] = {
- 0x00000000u, 0x77073096u, 0xee0e612cu, 0x990951bau, 0x076dc419u, 0x706af48fu, 0xe963a535u, 0x9e6495a3u,
- 0x0edb8832u, 0x79dcb8a4u, 0xe0d5e91eu, 0x97d2d988u, 0x09b64c2bu, 0x7eb17cbdu, 0xe7b82d07u, 0x90bf1d91u,
- 0x1db71064u, 0x6ab020f2u, 0xf3b97148u, 0x84be41deu, 0x1adad47du, 0x6ddde4ebu, 0xf4d4b551u, 0x83d385c7u,
- 0x136c9856u, 0x646ba8c0u, 0xfd62f97au, 0x8a65c9ecu, 0x14015c4fu, 0x63066cd9u, 0xfa0f3d63u, 0x8d080df5u,
- 0x3b6e20c8u, 0x4c69105eu, 0xd56041e4u, 0xa2677172u, 0x3c03e4d1u, 0x4b04d447u, 0xd20d85fdu, 0xa50ab56bu,
- 0x35b5a8fau, 0x42b2986cu, 0xdbbbc9d6u, 0xacbcf940u, 0x32d86ce3u, 0x45df5c75u, 0xdcd60dcfu, 0xabd13d59u,
- 0x26d930acu, 0x51de003au, 0xc8d75180u, 0xbfd06116u, 0x21b4f4b5u, 0x56b3c423u, 0xcfba9599u, 0xb8bda50fu,
- 0x2802b89eu, 0x5f058808u, 0xc60cd9b2u, 0xb10be924u, 0x2f6f7c87u, 0x58684c11u, 0xc1611dabu, 0xb6662d3du,
- 0x76dc4190u, 0x01db7106u, 0x98d220bcu, 0xefd5102au, 0x71b18589u, 0x06b6b51fu, 0x9fbfe4a5u, 0xe8b8d433u,
- 0x7807c9a2u, 0x0f00f934u, 0x9609a88eu, 0xe10e9818u, 0x7f6a0dbbu, 0x086d3d2du, 0x91646c97u, 0xe6635c01u,
- 0x6b6b51f4u, 0x1c6c6162u, 0x856530d8u, 0xf262004eu, 0x6c0695edu, 0x1b01a57bu, 0x8208f4c1u, 0xf50fc457u,
- 0x65b0d9c6u, 0x12b7e950u, 0x8bbeb8eau, 0xfcb9887cu, 0x62dd1ddfu, 0x15da2d49u, 0x8cd37cf3u, 0xfbd44c65u,
- 0x4db26158u, 0x3ab551ceu, 0xa3bc0074u, 0xd4bb30e2u, 0x4adfa541u, 0x3dd895d7u, 0xa4d1c46du, 0xd3d6f4fbu,
- 0x4369e96au, 0x346ed9fcu, 0xad678846u, 0xda60b8d0u, 0x44042d73u, 0x33031de5u, 0xaa0a4c5fu, 0xdd0d7cc9u,
- 0x5005713cu, 0x270241aau, 0xbe0b1010u, 0xc90c2086u, 0x5768b525u, 0x206f85b3u, 0xb966d409u, 0xce61e49fu,
- 0x5edef90eu, 0x29d9c998u, 0xb0d09822u, 0xc7d7a8b4u, 0x59b33d17u, 0x2eb40d81u, 0xb7bd5c3bu, 0xc0ba6cadu,
- 0xedb88320u, 0x9abfb3b6u, 0x03b6e20cu, 0x74b1d29au, 0xead54739u, 0x9dd277afu, 0x04db2615u, 0x73dc1683u,
- 0xe3630b12u, 0x94643b84u, 0x0d6d6a3eu, 0x7a6a5aa8u, 0xe40ecf0bu, 0x9309ff9du, 0x0a00ae27u, 0x7d079eb1u,
- 0xf00f9344u, 0x8708a3d2u, 0x1e01f268u, 0x6906c2feu, 0xf762575du, 0x806567cbu, 0x196c3671u, 0x6e6b06e7u,
- 0xfed41b76u, 0x89d32be0u, 0x10da7a5au, 0x67dd4accu, 0xf9b9df6fu, 0x8ebeeff9u, 0x17b7be43u, 0x60b08ed5u,
- 0xd6d6a3e8u, 0xa1d1937eu, 0x38d8c2c4u, 0x4fdff252u, 0xd1bb67f1u, 0xa6bc5767u, 0x3fb506ddu, 0x48b2364bu,
- 0xd80d2bdau, 0xaf0a1b4cu, 0x36034af6u, 0x41047a60u, 0xdf60efc3u, 0xa867df55u, 0x316e8eefu, 0x4669be79u,
- 0xcb61b38cu, 0xbc66831au, 0x256fd2a0u, 0x5268e236u, 0xcc0c7795u, 0xbb0b4703u, 0x220216b9u, 0x5505262fu,
- 0xc5ba3bbeu, 0xb2bd0b28u, 0x2bb45a92u, 0x5cb36a04u, 0xc2d7ffa7u, 0xb5d0cf31u, 0x2cd99e8bu, 0x5bdeae1du,
- 0x9b64c2b0u, 0xec63f226u, 0x756aa39cu, 0x026d930au, 0x9c0906a9u, 0xeb0e363fu, 0x72076785u, 0x05005713u,
- 0x95bf4a82u, 0xe2b87a14u, 0x7bb12baeu, 0x0cb61b38u, 0x92d28e9bu, 0xe5d5be0du, 0x7cdcefb7u, 0x0bdbdf21u,
- 0x86d3d2d4u, 0xf1d4e242u, 0x68ddb3f8u, 0x1fda836eu, 0x81be16cdu, 0xf6b9265bu, 0x6fb077e1u, 0x18b74777u,
- 0x88085ae6u, 0xff0f6a70u, 0x66063bcau, 0x11010b5cu, 0x8f659effu, 0xf862ae69u, 0x616bffd3u, 0x166ccf45u,
- 0xa00ae278u, 0xd70dd2eeu, 0x4e048354u, 0x3903b3c2u, 0xa7672661u, 0xd06016f7u, 0x4969474du, 0x3e6e77dbu,
- 0xaed16a4au, 0xd9d65adcu, 0x40df0b66u, 0x37d83bf0u, 0xa9bcae53u, 0xdebb9ec5u, 0x47b2cf7fu, 0x30b5ffe9u,
- 0xbdbdf21cu, 0xcabac28au, 0x53b39330u, 0x24b4a3a6u, 0xbad03605u, 0xcdd70693u, 0x54de5729u, 0x23d967bfu,
- 0xb3667a2eu, 0xc4614ab8u, 0x5d681b02u, 0x2a6f2b94u, 0xb40bbe37u, 0xc30c8ea1u, 0x5a05df1bu, 0x2d02ef8du
-};
-
-static const unsigned lodepng_crc32_table1[256] = {
- 0x00000000u, 0x191b3141u, 0x32366282u, 0x2b2d53c3u, 0x646cc504u, 0x7d77f445u, 0x565aa786u, 0x4f4196c7u,
- 0xc8d98a08u, 0xd1c2bb49u, 0xfaefe88au, 0xe3f4d9cbu, 0xacb54f0cu, 0xb5ae7e4du, 0x9e832d8eu, 0x87981ccfu,
- 0x4ac21251u, 0x53d92310u, 0x78f470d3u, 0x61ef4192u, 0x2eaed755u, 0x37b5e614u, 0x1c98b5d7u, 0x05838496u,
- 0x821b9859u, 0x9b00a918u, 0xb02dfadbu, 0xa936cb9au, 0xe6775d5du, 0xff6c6c1cu, 0xd4413fdfu, 0xcd5a0e9eu,
- 0x958424a2u, 0x8c9f15e3u, 0xa7b24620u, 0xbea97761u, 0xf1e8e1a6u, 0xe8f3d0e7u, 0xc3de8324u, 0xdac5b265u,
- 0x5d5daeaau, 0x44469febu, 0x6f6bcc28u, 0x7670fd69u, 0x39316baeu, 0x202a5aefu, 0x0b07092cu, 0x121c386du,
- 0xdf4636f3u, 0xc65d07b2u, 0xed705471u, 0xf46b6530u, 0xbb2af3f7u, 0xa231c2b6u, 0x891c9175u, 0x9007a034u,
- 0x179fbcfbu, 0x0e848dbau, 0x25a9de79u, 0x3cb2ef38u, 0x73f379ffu, 0x6ae848beu, 0x41c51b7du, 0x58de2a3cu,
- 0xf0794f05u, 0xe9627e44u, 0xc24f2d87u, 0xdb541cc6u, 0x94158a01u, 0x8d0ebb40u, 0xa623e883u, 0xbf38d9c2u,
- 0x38a0c50du, 0x21bbf44cu, 0x0a96a78fu, 0x138d96ceu, 0x5ccc0009u, 0x45d73148u, 0x6efa628bu, 0x77e153cau,
- 0xbabb5d54u, 0xa3a06c15u, 0x888d3fd6u, 0x91960e97u, 0xded79850u, 0xc7cca911u, 0xece1fad2u, 0xf5facb93u,
- 0x7262d75cu, 0x6b79e61du, 0x4054b5deu, 0x594f849fu, 0x160e1258u, 0x0f152319u, 0x243870dau, 0x3d23419bu,
- 0x65fd6ba7u, 0x7ce65ae6u, 0x57cb0925u, 0x4ed03864u, 0x0191aea3u, 0x188a9fe2u, 0x33a7cc21u, 0x2abcfd60u,
- 0xad24e1afu, 0xb43fd0eeu, 0x9f12832du, 0x8609b26cu, 0xc94824abu, 0xd05315eau, 0xfb7e4629u, 0xe2657768u,
- 0x2f3f79f6u, 0x362448b7u, 0x1d091b74u, 0x04122a35u, 0x4b53bcf2u, 0x52488db3u, 0x7965de70u, 0x607eef31u,
- 0xe7e6f3feu, 0xfefdc2bfu, 0xd5d0917cu, 0xcccba03du, 0x838a36fau, 0x9a9107bbu, 0xb1bc5478u, 0xa8a76539u,
- 0x3b83984bu, 0x2298a90au, 0x09b5fac9u, 0x10aecb88u, 0x5fef5d4fu, 0x46f46c0eu, 0x6dd93fcdu, 0x74c20e8cu,
- 0xf35a1243u, 0xea412302u, 0xc16c70c1u, 0xd8774180u, 0x9736d747u, 0x8e2de606u, 0xa500b5c5u, 0xbc1b8484u,
- 0x71418a1au, 0x685abb5bu, 0x4377e898u, 0x5a6cd9d9u, 0x152d4f1eu, 0x0c367e5fu, 0x271b2d9cu, 0x3e001cddu,
- 0xb9980012u, 0xa0833153u, 0x8bae6290u, 0x92b553d1u, 0xddf4c516u, 0xc4eff457u, 0xefc2a794u, 0xf6d996d5u,
- 0xae07bce9u, 0xb71c8da8u, 0x9c31de6bu, 0x852aef2au, 0xca6b79edu, 0xd37048acu, 0xf85d1b6fu, 0xe1462a2eu,
- 0x66de36e1u, 0x7fc507a0u, 0x54e85463u, 0x4df36522u, 0x02b2f3e5u, 0x1ba9c2a4u, 0x30849167u, 0x299fa026u,
- 0xe4c5aeb8u, 0xfdde9ff9u, 0xd6f3cc3au, 0xcfe8fd7bu, 0x80a96bbcu, 0x99b25afdu, 0xb29f093eu, 0xab84387fu,
- 0x2c1c24b0u, 0x350715f1u, 0x1e2a4632u, 0x07317773u, 0x4870e1b4u, 0x516bd0f5u, 0x7a468336u, 0x635db277u,
- 0xcbfad74eu, 0xd2e1e60fu, 0xf9ccb5ccu, 0xe0d7848du, 0xaf96124au, 0xb68d230bu, 0x9da070c8u, 0x84bb4189u,
- 0x03235d46u, 0x1a386c07u, 0x31153fc4u, 0x280e0e85u, 0x674f9842u, 0x7e54a903u, 0x5579fac0u, 0x4c62cb81u,
- 0x8138c51fu, 0x9823f45eu, 0xb30ea79du, 0xaa1596dcu, 0xe554001bu, 0xfc4f315au, 0xd7626299u, 0xce7953d8u,
- 0x49e14f17u, 0x50fa7e56u, 0x7bd72d95u, 0x62cc1cd4u, 0x2d8d8a13u, 0x3496bb52u, 0x1fbbe891u, 0x06a0d9d0u,
- 0x5e7ef3ecu, 0x4765c2adu, 0x6c48916eu, 0x7553a02fu, 0x3a1236e8u, 0x230907a9u, 0x0824546au, 0x113f652bu,
- 0x96a779e4u, 0x8fbc48a5u, 0xa4911b66u, 0xbd8a2a27u, 0xf2cbbce0u, 0xebd08da1u, 0xc0fdde62u, 0xd9e6ef23u,
- 0x14bce1bdu, 0x0da7d0fcu, 0x268a833fu, 0x3f91b27eu, 0x70d024b9u, 0x69cb15f8u, 0x42e6463bu, 0x5bfd777au,
- 0xdc656bb5u, 0xc57e5af4u, 0xee530937u, 0xf7483876u, 0xb809aeb1u, 0xa1129ff0u, 0x8a3fcc33u, 0x9324fd72u
-};
-
-static const unsigned lodepng_crc32_table2[256] = {
- 0x00000000u, 0x01c26a37u, 0x0384d46eu, 0x0246be59u, 0x0709a8dcu, 0x06cbc2ebu, 0x048d7cb2u, 0x054f1685u,
- 0x0e1351b8u, 0x0fd13b8fu, 0x0d9785d6u, 0x0c55efe1u, 0x091af964u, 0x08d89353u, 0x0a9e2d0au, 0x0b5c473du,
- 0x1c26a370u, 0x1de4c947u, 0x1fa2771eu, 0x1e601d29u, 0x1b2f0bacu, 0x1aed619bu, 0x18abdfc2u, 0x1969b5f5u,
- 0x1235f2c8u, 0x13f798ffu, 0x11b126a6u, 0x10734c91u, 0x153c5a14u, 0x14fe3023u, 0x16b88e7au, 0x177ae44du,
- 0x384d46e0u, 0x398f2cd7u, 0x3bc9928eu, 0x3a0bf8b9u, 0x3f44ee3cu, 0x3e86840bu, 0x3cc03a52u, 0x3d025065u,
- 0x365e1758u, 0x379c7d6fu, 0x35dac336u, 0x3418a901u, 0x3157bf84u, 0x3095d5b3u, 0x32d36beau, 0x331101ddu,
- 0x246be590u, 0x25a98fa7u, 0x27ef31feu, 0x262d5bc9u, 0x23624d4cu, 0x22a0277bu, 0x20e69922u, 0x2124f315u,
- 0x2a78b428u, 0x2bbade1fu, 0x29fc6046u, 0x283e0a71u, 0x2d711cf4u, 0x2cb376c3u, 0x2ef5c89au, 0x2f37a2adu,
- 0x709a8dc0u, 0x7158e7f7u, 0x731e59aeu, 0x72dc3399u, 0x7793251cu, 0x76514f2bu, 0x7417f172u, 0x75d59b45u,
- 0x7e89dc78u, 0x7f4bb64fu, 0x7d0d0816u, 0x7ccf6221u, 0x798074a4u, 0x78421e93u, 0x7a04a0cau, 0x7bc6cafdu,
- 0x6cbc2eb0u, 0x6d7e4487u, 0x6f38fadeu, 0x6efa90e9u, 0x6bb5866cu, 0x6a77ec5bu, 0x68315202u, 0x69f33835u,
- 0x62af7f08u, 0x636d153fu, 0x612bab66u, 0x60e9c151u, 0x65a6d7d4u, 0x6464bde3u, 0x662203bau, 0x67e0698du,
- 0x48d7cb20u, 0x4915a117u, 0x4b531f4eu, 0x4a917579u, 0x4fde63fcu, 0x4e1c09cbu, 0x4c5ab792u, 0x4d98dda5u,
- 0x46c49a98u, 0x4706f0afu, 0x45404ef6u, 0x448224c1u, 0x41cd3244u, 0x400f5873u, 0x4249e62au, 0x438b8c1du,
- 0x54f16850u, 0x55330267u, 0x5775bc3eu, 0x56b7d609u, 0x53f8c08cu, 0x523aaabbu, 0x507c14e2u, 0x51be7ed5u,
- 0x5ae239e8u, 0x5b2053dfu, 0x5966ed86u, 0x58a487b1u, 0x5deb9134u, 0x5c29fb03u, 0x5e6f455au, 0x5fad2f6du,
- 0xe1351b80u, 0xe0f771b7u, 0xe2b1cfeeu, 0xe373a5d9u, 0xe63cb35cu, 0xe7fed96bu, 0xe5b86732u, 0xe47a0d05u,
- 0xef264a38u, 0xeee4200fu, 0xeca29e56u, 0xed60f461u, 0xe82fe2e4u, 0xe9ed88d3u, 0xebab368au, 0xea695cbdu,
- 0xfd13b8f0u, 0xfcd1d2c7u, 0xfe976c9eu, 0xff5506a9u, 0xfa1a102cu, 0xfbd87a1bu, 0xf99ec442u, 0xf85cae75u,
- 0xf300e948u, 0xf2c2837fu, 0xf0843d26u, 0xf1465711u, 0xf4094194u, 0xf5cb2ba3u, 0xf78d95fau, 0xf64fffcdu,
- 0xd9785d60u, 0xd8ba3757u, 0xdafc890eu, 0xdb3ee339u, 0xde71f5bcu, 0xdfb39f8bu, 0xddf521d2u, 0xdc374be5u,
- 0xd76b0cd8u, 0xd6a966efu, 0xd4efd8b6u, 0xd52db281u, 0xd062a404u, 0xd1a0ce33u, 0xd3e6706au, 0xd2241a5du,
- 0xc55efe10u, 0xc49c9427u, 0xc6da2a7eu, 0xc7184049u, 0xc25756ccu, 0xc3953cfbu, 0xc1d382a2u, 0xc011e895u,
- 0xcb4dafa8u, 0xca8fc59fu, 0xc8c97bc6u, 0xc90b11f1u, 0xcc440774u, 0xcd866d43u, 0xcfc0d31au, 0xce02b92du,
- 0x91af9640u, 0x906dfc77u, 0x922b422eu, 0x93e92819u, 0x96a63e9cu, 0x976454abu, 0x9522eaf2u, 0x94e080c5u,
- 0x9fbcc7f8u, 0x9e7eadcfu, 0x9c381396u, 0x9dfa79a1u, 0x98b56f24u, 0x99770513u, 0x9b31bb4au, 0x9af3d17du,
- 0x8d893530u, 0x8c4b5f07u, 0x8e0de15eu, 0x8fcf8b69u, 0x8a809decu, 0x8b42f7dbu, 0x89044982u, 0x88c623b5u,
- 0x839a6488u, 0x82580ebfu, 0x801eb0e6u, 0x81dcdad1u, 0x8493cc54u, 0x8551a663u, 0x8717183au, 0x86d5720du,
- 0xa9e2d0a0u, 0xa820ba97u, 0xaa6604ceu, 0xaba46ef9u, 0xaeeb787cu, 0xaf29124bu, 0xad6fac12u, 0xacadc625u,
- 0xa7f18118u, 0xa633eb2fu, 0xa4755576u, 0xa5b73f41u, 0xa0f829c4u, 0xa13a43f3u, 0xa37cfdaau, 0xa2be979du,
- 0xb5c473d0u, 0xb40619e7u, 0xb640a7beu, 0xb782cd89u, 0xb2cddb0cu, 0xb30fb13bu, 0xb1490f62u, 0xb08b6555u,
- 0xbbd72268u, 0xba15485fu, 0xb853f606u, 0xb9919c31u, 0xbcde8ab4u, 0xbd1ce083u, 0xbf5a5edau, 0xbe9834edu
-};
-
-static const unsigned lodepng_crc32_table3[256] = {
- 0x00000000u, 0xb8bc6765u, 0xaa09c88bu, 0x12b5afeeu, 0x8f629757u, 0x37def032u, 0x256b5fdcu, 0x9dd738b9u,
- 0xc5b428efu, 0x7d084f8au, 0x6fbde064u, 0xd7018701u, 0x4ad6bfb8u, 0xf26ad8ddu, 0xe0df7733u, 0x58631056u,
- 0x5019579fu, 0xe8a530fau, 0xfa109f14u, 0x42acf871u, 0xdf7bc0c8u, 0x67c7a7adu, 0x75720843u, 0xcdce6f26u,
- 0x95ad7f70u, 0x2d111815u, 0x3fa4b7fbu, 0x8718d09eu, 0x1acfe827u, 0xa2738f42u, 0xb0c620acu, 0x087a47c9u,
- 0xa032af3eu, 0x188ec85bu, 0x0a3b67b5u, 0xb28700d0u, 0x2f503869u, 0x97ec5f0cu, 0x8559f0e2u, 0x3de59787u,
- 0x658687d1u, 0xdd3ae0b4u, 0xcf8f4f5au, 0x7733283fu, 0xeae41086u, 0x525877e3u, 0x40edd80du, 0xf851bf68u,
- 0xf02bf8a1u, 0x48979fc4u, 0x5a22302au, 0xe29e574fu, 0x7f496ff6u, 0xc7f50893u, 0xd540a77du, 0x6dfcc018u,
- 0x359fd04eu, 0x8d23b72bu, 0x9f9618c5u, 0x272a7fa0u, 0xbafd4719u, 0x0241207cu, 0x10f48f92u, 0xa848e8f7u,
- 0x9b14583du, 0x23a83f58u, 0x311d90b6u, 0x89a1f7d3u, 0x1476cf6au, 0xaccaa80fu, 0xbe7f07e1u, 0x06c36084u,
- 0x5ea070d2u, 0xe61c17b7u, 0xf4a9b859u, 0x4c15df3cu, 0xd1c2e785u, 0x697e80e0u, 0x7bcb2f0eu, 0xc377486bu,
- 0xcb0d0fa2u, 0x73b168c7u, 0x6104c729u, 0xd9b8a04cu, 0x446f98f5u, 0xfcd3ff90u, 0xee66507eu, 0x56da371bu,
- 0x0eb9274du, 0xb6054028u, 0xa4b0efc6u, 0x1c0c88a3u, 0x81dbb01au, 0x3967d77fu, 0x2bd27891u, 0x936e1ff4u,
- 0x3b26f703u, 0x839a9066u, 0x912f3f88u, 0x299358edu, 0xb4446054u, 0x0cf80731u, 0x1e4da8dfu, 0xa6f1cfbau,
- 0xfe92dfecu, 0x462eb889u, 0x549b1767u, 0xec277002u, 0x71f048bbu, 0xc94c2fdeu, 0xdbf98030u, 0x6345e755u,
- 0x6b3fa09cu, 0xd383c7f9u, 0xc1366817u, 0x798a0f72u, 0xe45d37cbu, 0x5ce150aeu, 0x4e54ff40u, 0xf6e89825u,
- 0xae8b8873u, 0x1637ef16u, 0x048240f8u, 0xbc3e279du, 0x21e91f24u, 0x99557841u, 0x8be0d7afu, 0x335cb0cau,
- 0xed59b63bu, 0x55e5d15eu, 0x47507eb0u, 0xffec19d5u, 0x623b216cu, 0xda874609u, 0xc832e9e7u, 0x708e8e82u,
- 0x28ed9ed4u, 0x9051f9b1u, 0x82e4565fu, 0x3a58313au, 0xa78f0983u, 0x1f336ee6u, 0x0d86c108u, 0xb53aa66du,
- 0xbd40e1a4u, 0x05fc86c1u, 0x1749292fu, 0xaff54e4au, 0x322276f3u, 0x8a9e1196u, 0x982bbe78u, 0x2097d91du,
- 0x78f4c94bu, 0xc048ae2eu, 0xd2fd01c0u, 0x6a4166a5u, 0xf7965e1cu, 0x4f2a3979u, 0x5d9f9697u, 0xe523f1f2u,
- 0x4d6b1905u, 0xf5d77e60u, 0xe762d18eu, 0x5fdeb6ebu, 0xc2098e52u, 0x7ab5e937u, 0x680046d9u, 0xd0bc21bcu,
- 0x88df31eau, 0x3063568fu, 0x22d6f961u, 0x9a6a9e04u, 0x07bda6bdu, 0xbf01c1d8u, 0xadb46e36u, 0x15080953u,
- 0x1d724e9au, 0xa5ce29ffu, 0xb77b8611u, 0x0fc7e174u, 0x9210d9cdu, 0x2aacbea8u, 0x38191146u, 0x80a57623u,
- 0xd8c66675u, 0x607a0110u, 0x72cfaefeu, 0xca73c99bu, 0x57a4f122u, 0xef189647u, 0xfdad39a9u, 0x45115eccu,
- 0x764dee06u, 0xcef18963u, 0xdc44268du, 0x64f841e8u, 0xf92f7951u, 0x41931e34u, 0x5326b1dau, 0xeb9ad6bfu,
- 0xb3f9c6e9u, 0x0b45a18cu, 0x19f00e62u, 0xa14c6907u, 0x3c9b51beu, 0x842736dbu, 0x96929935u, 0x2e2efe50u,
- 0x2654b999u, 0x9ee8defcu, 0x8c5d7112u, 0x34e11677u, 0xa9362eceu, 0x118a49abu, 0x033fe645u, 0xbb838120u,
- 0xe3e09176u, 0x5b5cf613u, 0x49e959fdu, 0xf1553e98u, 0x6c820621u, 0xd43e6144u, 0xc68bceaau, 0x7e37a9cfu,
- 0xd67f4138u, 0x6ec3265du, 0x7c7689b3u, 0xc4caeed6u, 0x591dd66fu, 0xe1a1b10au, 0xf3141ee4u, 0x4ba87981u,
- 0x13cb69d7u, 0xab770eb2u, 0xb9c2a15cu, 0x017ec639u, 0x9ca9fe80u, 0x241599e5u, 0x36a0360bu, 0x8e1c516eu,
- 0x866616a7u, 0x3eda71c2u, 0x2c6fde2cu, 0x94d3b949u, 0x090481f0u, 0xb1b8e695u, 0xa30d497bu, 0x1bb12e1eu,
- 0x43d23e48u, 0xfb6e592du, 0xe9dbf6c3u, 0x516791a6u, 0xccb0a91fu, 0x740cce7au, 0x66b96194u, 0xde0506f1u
-};
-
-static const unsigned lodepng_crc32_table4[256] = {
- 0x00000000u, 0x3d6029b0u, 0x7ac05360u, 0x47a07ad0u, 0xf580a6c0u, 0xc8e08f70u, 0x8f40f5a0u, 0xb220dc10u,
- 0x30704bc1u, 0x0d106271u, 0x4ab018a1u, 0x77d03111u, 0xc5f0ed01u, 0xf890c4b1u, 0xbf30be61u, 0x825097d1u,
- 0x60e09782u, 0x5d80be32u, 0x1a20c4e2u, 0x2740ed52u, 0x95603142u, 0xa80018f2u, 0xefa06222u, 0xd2c04b92u,
- 0x5090dc43u, 0x6df0f5f3u, 0x2a508f23u, 0x1730a693u, 0xa5107a83u, 0x98705333u, 0xdfd029e3u, 0xe2b00053u,
- 0xc1c12f04u, 0xfca106b4u, 0xbb017c64u, 0x866155d4u, 0x344189c4u, 0x0921a074u, 0x4e81daa4u, 0x73e1f314u,
- 0xf1b164c5u, 0xccd14d75u, 0x8b7137a5u, 0xb6111e15u, 0x0431c205u, 0x3951ebb5u, 0x7ef19165u, 0x4391b8d5u,
- 0xa121b886u, 0x9c419136u, 0xdbe1ebe6u, 0xe681c256u, 0x54a11e46u, 0x69c137f6u, 0x2e614d26u, 0x13016496u,
- 0x9151f347u, 0xac31daf7u, 0xeb91a027u, 0xd6f18997u, 0x64d15587u, 0x59b17c37u, 0x1e1106e7u, 0x23712f57u,
- 0x58f35849u, 0x659371f9u, 0x22330b29u, 0x1f532299u, 0xad73fe89u, 0x9013d739u, 0xd7b3ade9u, 0xead38459u,
- 0x68831388u, 0x55e33a38u, 0x124340e8u, 0x2f236958u, 0x9d03b548u, 0xa0639cf8u, 0xe7c3e628u, 0xdaa3cf98u,
- 0x3813cfcbu, 0x0573e67bu, 0x42d39cabu, 0x7fb3b51bu, 0xcd93690bu, 0xf0f340bbu, 0xb7533a6bu, 0x8a3313dbu,
- 0x0863840au, 0x3503adbau, 0x72a3d76au, 0x4fc3fedau, 0xfde322cau, 0xc0830b7au, 0x872371aau, 0xba43581au,
- 0x9932774du, 0xa4525efdu, 0xe3f2242du, 0xde920d9du, 0x6cb2d18du, 0x51d2f83du, 0x167282edu, 0x2b12ab5du,
- 0xa9423c8cu, 0x9422153cu, 0xd3826fecu, 0xeee2465cu, 0x5cc29a4cu, 0x61a2b3fcu, 0x2602c92cu, 0x1b62e09cu,
- 0xf9d2e0cfu, 0xc4b2c97fu, 0x8312b3afu, 0xbe729a1fu, 0x0c52460fu, 0x31326fbfu, 0x7692156fu, 0x4bf23cdfu,
- 0xc9a2ab0eu, 0xf4c282beu, 0xb362f86eu, 0x8e02d1deu, 0x3c220dceu, 0x0142247eu, 0x46e25eaeu, 0x7b82771eu,
- 0xb1e6b092u, 0x8c869922u, 0xcb26e3f2u, 0xf646ca42u, 0x44661652u, 0x79063fe2u, 0x3ea64532u, 0x03c66c82u,
- 0x8196fb53u, 0xbcf6d2e3u, 0xfb56a833u, 0xc6368183u, 0x74165d93u, 0x49767423u, 0x0ed60ef3u, 0x33b62743u,
- 0xd1062710u, 0xec660ea0u, 0xabc67470u, 0x96a65dc0u, 0x248681d0u, 0x19e6a860u, 0x5e46d2b0u, 0x6326fb00u,
- 0xe1766cd1u, 0xdc164561u, 0x9bb63fb1u, 0xa6d61601u, 0x14f6ca11u, 0x2996e3a1u, 0x6e369971u, 0x5356b0c1u,
- 0x70279f96u, 0x4d47b626u, 0x0ae7ccf6u, 0x3787e546u, 0x85a73956u, 0xb8c710e6u, 0xff676a36u, 0xc2074386u,
- 0x4057d457u, 0x7d37fde7u, 0x3a978737u, 0x07f7ae87u, 0xb5d77297u, 0x88b75b27u, 0xcf1721f7u, 0xf2770847u,
- 0x10c70814u, 0x2da721a4u, 0x6a075b74u, 0x576772c4u, 0xe547aed4u, 0xd8278764u, 0x9f87fdb4u, 0xa2e7d404u,
- 0x20b743d5u, 0x1dd76a65u, 0x5a7710b5u, 0x67173905u, 0xd537e515u, 0xe857cca5u, 0xaff7b675u, 0x92979fc5u,
- 0xe915e8dbu, 0xd475c16bu, 0x93d5bbbbu, 0xaeb5920bu, 0x1c954e1bu, 0x21f567abu, 0x66551d7bu, 0x5b3534cbu,
- 0xd965a31au, 0xe4058aaau, 0xa3a5f07au, 0x9ec5d9cau, 0x2ce505dau, 0x11852c6au, 0x562556bau, 0x6b457f0au,
- 0x89f57f59u, 0xb49556e9u, 0xf3352c39u, 0xce550589u, 0x7c75d999u, 0x4115f029u, 0x06b58af9u, 0x3bd5a349u,
- 0xb9853498u, 0x84e51d28u, 0xc34567f8u, 0xfe254e48u, 0x4c059258u, 0x7165bbe8u, 0x36c5c138u, 0x0ba5e888u,
- 0x28d4c7dfu, 0x15b4ee6fu, 0x521494bfu, 0x6f74bd0fu, 0xdd54611fu, 0xe03448afu, 0xa794327fu, 0x9af41bcfu,
- 0x18a48c1eu, 0x25c4a5aeu, 0x6264df7eu, 0x5f04f6ceu, 0xed242adeu, 0xd044036eu, 0x97e479beu, 0xaa84500eu,
- 0x4834505du, 0x755479edu, 0x32f4033du, 0x0f942a8du, 0xbdb4f69du, 0x80d4df2du, 0xc774a5fdu, 0xfa148c4du,
- 0x78441b9cu, 0x4524322cu, 0x028448fcu, 0x3fe4614cu, 0x8dc4bd5cu, 0xb0a494ecu, 0xf704ee3cu, 0xca64c78cu
-};
-
-static const unsigned lodepng_crc32_table5[256] = {
- 0x00000000u, 0xcb5cd3a5u, 0x4dc8a10bu, 0x869472aeu, 0x9b914216u, 0x50cd91b3u, 0xd659e31du, 0x1d0530b8u,
- 0xec53826du, 0x270f51c8u, 0xa19b2366u, 0x6ac7f0c3u, 0x77c2c07bu, 0xbc9e13deu, 0x3a0a6170u, 0xf156b2d5u,
- 0x03d6029bu, 0xc88ad13eu, 0x4e1ea390u, 0x85427035u, 0x9847408du, 0x531b9328u, 0xd58fe186u, 0x1ed33223u,
- 0xef8580f6u, 0x24d95353u, 0xa24d21fdu, 0x6911f258u, 0x7414c2e0u, 0xbf481145u, 0x39dc63ebu, 0xf280b04eu,
- 0x07ac0536u, 0xccf0d693u, 0x4a64a43du, 0x81387798u, 0x9c3d4720u, 0x57619485u, 0xd1f5e62bu, 0x1aa9358eu,
- 0xebff875bu, 0x20a354feu, 0xa6372650u, 0x6d6bf5f5u, 0x706ec54du, 0xbb3216e8u, 0x3da66446u, 0xf6fab7e3u,
- 0x047a07adu, 0xcf26d408u, 0x49b2a6a6u, 0x82ee7503u, 0x9feb45bbu, 0x54b7961eu, 0xd223e4b0u, 0x197f3715u,
- 0xe82985c0u, 0x23755665u, 0xa5e124cbu, 0x6ebdf76eu, 0x73b8c7d6u, 0xb8e41473u, 0x3e7066ddu, 0xf52cb578u,
- 0x0f580a6cu, 0xc404d9c9u, 0x4290ab67u, 0x89cc78c2u, 0x94c9487au, 0x5f959bdfu, 0xd901e971u, 0x125d3ad4u,
- 0xe30b8801u, 0x28575ba4u, 0xaec3290au, 0x659ffaafu, 0x789aca17u, 0xb3c619b2u, 0x35526b1cu, 0xfe0eb8b9u,
- 0x0c8e08f7u, 0xc7d2db52u, 0x4146a9fcu, 0x8a1a7a59u, 0x971f4ae1u, 0x5c439944u, 0xdad7ebeau, 0x118b384fu,
- 0xe0dd8a9au, 0x2b81593fu, 0xad152b91u, 0x6649f834u, 0x7b4cc88cu, 0xb0101b29u, 0x36846987u, 0xfdd8ba22u,
- 0x08f40f5au, 0xc3a8dcffu, 0x453cae51u, 0x8e607df4u, 0x93654d4cu, 0x58399ee9u, 0xdeadec47u, 0x15f13fe2u,
- 0xe4a78d37u, 0x2ffb5e92u, 0xa96f2c3cu, 0x6233ff99u, 0x7f36cf21u, 0xb46a1c84u, 0x32fe6e2au, 0xf9a2bd8fu,
- 0x0b220dc1u, 0xc07ede64u, 0x46eaaccau, 0x8db67f6fu, 0x90b34fd7u, 0x5bef9c72u, 0xdd7beedcu, 0x16273d79u,
- 0xe7718facu, 0x2c2d5c09u, 0xaab92ea7u, 0x61e5fd02u, 0x7ce0cdbau, 0xb7bc1e1fu, 0x31286cb1u, 0xfa74bf14u,
- 0x1eb014d8u, 0xd5ecc77du, 0x5378b5d3u, 0x98246676u, 0x852156ceu, 0x4e7d856bu, 0xc8e9f7c5u, 0x03b52460u,
- 0xf2e396b5u, 0x39bf4510u, 0xbf2b37beu, 0x7477e41bu, 0x6972d4a3u, 0xa22e0706u, 0x24ba75a8u, 0xefe6a60du,
- 0x1d661643u, 0xd63ac5e6u, 0x50aeb748u, 0x9bf264edu, 0x86f75455u, 0x4dab87f0u, 0xcb3ff55eu, 0x006326fbu,
- 0xf135942eu, 0x3a69478bu, 0xbcfd3525u, 0x77a1e680u, 0x6aa4d638u, 0xa1f8059du, 0x276c7733u, 0xec30a496u,
- 0x191c11eeu, 0xd240c24bu, 0x54d4b0e5u, 0x9f886340u, 0x828d53f8u, 0x49d1805du, 0xcf45f2f3u, 0x04192156u,
- 0xf54f9383u, 0x3e134026u, 0xb8873288u, 0x73dbe12du, 0x6eded195u, 0xa5820230u, 0x2316709eu, 0xe84aa33bu,
- 0x1aca1375u, 0xd196c0d0u, 0x5702b27eu, 0x9c5e61dbu, 0x815b5163u, 0x4a0782c6u, 0xcc93f068u, 0x07cf23cdu,
- 0xf6999118u, 0x3dc542bdu, 0xbb513013u, 0x700de3b6u, 0x6d08d30eu, 0xa65400abu, 0x20c07205u, 0xeb9ca1a0u,
- 0x11e81eb4u, 0xdab4cd11u, 0x5c20bfbfu, 0x977c6c1au, 0x8a795ca2u, 0x41258f07u, 0xc7b1fda9u, 0x0ced2e0cu,
- 0xfdbb9cd9u, 0x36e74f7cu, 0xb0733dd2u, 0x7b2fee77u, 0x662adecfu, 0xad760d6au, 0x2be27fc4u, 0xe0beac61u,
- 0x123e1c2fu, 0xd962cf8au, 0x5ff6bd24u, 0x94aa6e81u, 0x89af5e39u, 0x42f38d9cu, 0xc467ff32u, 0x0f3b2c97u,
- 0xfe6d9e42u, 0x35314de7u, 0xb3a53f49u, 0x78f9ececu, 0x65fcdc54u, 0xaea00ff1u, 0x28347d5fu, 0xe368aefau,
- 0x16441b82u, 0xdd18c827u, 0x5b8cba89u, 0x90d0692cu, 0x8dd55994u, 0x46898a31u, 0xc01df89fu, 0x0b412b3au,
- 0xfa1799efu, 0x314b4a4au, 0xb7df38e4u, 0x7c83eb41u, 0x6186dbf9u, 0xaada085cu, 0x2c4e7af2u, 0xe712a957u,
- 0x15921919u, 0xdececabcu, 0x585ab812u, 0x93066bb7u, 0x8e035b0fu, 0x455f88aau, 0xc3cbfa04u, 0x089729a1u,
- 0xf9c19b74u, 0x329d48d1u, 0xb4093a7fu, 0x7f55e9dau, 0x6250d962u, 0xa90c0ac7u, 0x2f987869u, 0xe4c4abccu
-};
-
-static const unsigned lodepng_crc32_table6[256] = {
- 0x00000000u, 0xa6770bb4u, 0x979f1129u, 0x31e81a9du, 0xf44f2413u, 0x52382fa7u, 0x63d0353au, 0xc5a73e8eu,
- 0x33ef4e67u, 0x959845d3u, 0xa4705f4eu, 0x020754fau, 0xc7a06a74u, 0x61d761c0u, 0x503f7b5du, 0xf64870e9u,
- 0x67de9cceu, 0xc1a9977au, 0xf0418de7u, 0x56368653u, 0x9391b8ddu, 0x35e6b369u, 0x040ea9f4u, 0xa279a240u,
- 0x5431d2a9u, 0xf246d91du, 0xc3aec380u, 0x65d9c834u, 0xa07ef6bau, 0x0609fd0eu, 0x37e1e793u, 0x9196ec27u,
- 0xcfbd399cu, 0x69ca3228u, 0x582228b5u, 0xfe552301u, 0x3bf21d8fu, 0x9d85163bu, 0xac6d0ca6u, 0x0a1a0712u,
- 0xfc5277fbu, 0x5a257c4fu, 0x6bcd66d2u, 0xcdba6d66u, 0x081d53e8u, 0xae6a585cu, 0x9f8242c1u, 0x39f54975u,
- 0xa863a552u, 0x0e14aee6u, 0x3ffcb47bu, 0x998bbfcfu, 0x5c2c8141u, 0xfa5b8af5u, 0xcbb39068u, 0x6dc49bdcu,
- 0x9b8ceb35u, 0x3dfbe081u, 0x0c13fa1cu, 0xaa64f1a8u, 0x6fc3cf26u, 0xc9b4c492u, 0xf85cde0fu, 0x5e2bd5bbu,
- 0x440b7579u, 0xe27c7ecdu, 0xd3946450u, 0x75e36fe4u, 0xb044516au, 0x16335adeu, 0x27db4043u, 0x81ac4bf7u,
- 0x77e43b1eu, 0xd19330aau, 0xe07b2a37u, 0x460c2183u, 0x83ab1f0du, 0x25dc14b9u, 0x14340e24u, 0xb2430590u,
- 0x23d5e9b7u, 0x85a2e203u, 0xb44af89eu, 0x123df32au, 0xd79acda4u, 0x71edc610u, 0x4005dc8du, 0xe672d739u,
- 0x103aa7d0u, 0xb64dac64u, 0x87a5b6f9u, 0x21d2bd4du, 0xe47583c3u, 0x42028877u, 0x73ea92eau, 0xd59d995eu,
- 0x8bb64ce5u, 0x2dc14751u, 0x1c295dccu, 0xba5e5678u, 0x7ff968f6u, 0xd98e6342u, 0xe86679dfu, 0x4e11726bu,
- 0xb8590282u, 0x1e2e0936u, 0x2fc613abu, 0x89b1181fu, 0x4c162691u, 0xea612d25u, 0xdb8937b8u, 0x7dfe3c0cu,
- 0xec68d02bu, 0x4a1fdb9fu, 0x7bf7c102u, 0xdd80cab6u, 0x1827f438u, 0xbe50ff8cu, 0x8fb8e511u, 0x29cfeea5u,
- 0xdf879e4cu, 0x79f095f8u, 0x48188f65u, 0xee6f84d1u, 0x2bc8ba5fu, 0x8dbfb1ebu, 0xbc57ab76u, 0x1a20a0c2u,
- 0x8816eaf2u, 0x2e61e146u, 0x1f89fbdbu, 0xb9fef06fu, 0x7c59cee1u, 0xda2ec555u, 0xebc6dfc8u, 0x4db1d47cu,
- 0xbbf9a495u, 0x1d8eaf21u, 0x2c66b5bcu, 0x8a11be08u, 0x4fb68086u, 0xe9c18b32u, 0xd82991afu, 0x7e5e9a1bu,
- 0xefc8763cu, 0x49bf7d88u, 0x78576715u, 0xde206ca1u, 0x1b87522fu, 0xbdf0599bu, 0x8c184306u, 0x2a6f48b2u,
- 0xdc27385bu, 0x7a5033efu, 0x4bb82972u, 0xedcf22c6u, 0x28681c48u, 0x8e1f17fcu, 0xbff70d61u, 0x198006d5u,
- 0x47abd36eu, 0xe1dcd8dau, 0xd034c247u, 0x7643c9f3u, 0xb3e4f77du, 0x1593fcc9u, 0x247be654u, 0x820cede0u,
- 0x74449d09u, 0xd23396bdu, 0xe3db8c20u, 0x45ac8794u, 0x800bb91au, 0x267cb2aeu, 0x1794a833u, 0xb1e3a387u,
- 0x20754fa0u, 0x86024414u, 0xb7ea5e89u, 0x119d553du, 0xd43a6bb3u, 0x724d6007u, 0x43a57a9au, 0xe5d2712eu,
- 0x139a01c7u, 0xb5ed0a73u, 0x840510eeu, 0x22721b5au, 0xe7d525d4u, 0x41a22e60u, 0x704a34fdu, 0xd63d3f49u,
- 0xcc1d9f8bu, 0x6a6a943fu, 0x5b828ea2u, 0xfdf58516u, 0x3852bb98u, 0x9e25b02cu, 0xafcdaab1u, 0x09baa105u,
- 0xfff2d1ecu, 0x5985da58u, 0x686dc0c5u, 0xce1acb71u, 0x0bbdf5ffu, 0xadcafe4bu, 0x9c22e4d6u, 0x3a55ef62u,
- 0xabc30345u, 0x0db408f1u, 0x3c5c126cu, 0x9a2b19d8u, 0x5f8c2756u, 0xf9fb2ce2u, 0xc813367fu, 0x6e643dcbu,
- 0x982c4d22u, 0x3e5b4696u, 0x0fb35c0bu, 0xa9c457bfu, 0x6c636931u, 0xca146285u, 0xfbfc7818u, 0x5d8b73acu,
- 0x03a0a617u, 0xa5d7ada3u, 0x943fb73eu, 0x3248bc8au, 0xf7ef8204u, 0x519889b0u, 0x6070932du, 0xc6079899u,
- 0x304fe870u, 0x9638e3c4u, 0xa7d0f959u, 0x01a7f2edu, 0xc400cc63u, 0x6277c7d7u, 0x539fdd4au, 0xf5e8d6feu,
- 0x647e3ad9u, 0xc209316du, 0xf3e12bf0u, 0x55962044u, 0x90311ecau, 0x3646157eu, 0x07ae0fe3u, 0xa1d90457u,
- 0x579174beu, 0xf1e67f0au, 0xc00e6597u, 0x66796e23u, 0xa3de50adu, 0x05a95b19u, 0x34414184u, 0x92364a30u
-};
-
-static const unsigned lodepng_crc32_table7[256] = {
- 0x00000000u, 0xccaa009eu, 0x4225077du, 0x8e8f07e3u, 0x844a0efau, 0x48e00e64u, 0xc66f0987u, 0x0ac50919u,
- 0xd3e51bb5u, 0x1f4f1b2bu, 0x91c01cc8u, 0x5d6a1c56u, 0x57af154fu, 0x9b0515d1u, 0x158a1232u, 0xd92012acu,
- 0x7cbb312bu, 0xb01131b5u, 0x3e9e3656u, 0xf23436c8u, 0xf8f13fd1u, 0x345b3f4fu, 0xbad438acu, 0x767e3832u,
- 0xaf5e2a9eu, 0x63f42a00u, 0xed7b2de3u, 0x21d12d7du, 0x2b142464u, 0xe7be24fau, 0x69312319u, 0xa59b2387u,
- 0xf9766256u, 0x35dc62c8u, 0xbb53652bu, 0x77f965b5u, 0x7d3c6cacu, 0xb1966c32u, 0x3f196bd1u, 0xf3b36b4fu,
- 0x2a9379e3u, 0xe639797du, 0x68b67e9eu, 0xa41c7e00u, 0xaed97719u, 0x62737787u, 0xecfc7064u, 0x205670fau,
- 0x85cd537du, 0x496753e3u, 0xc7e85400u, 0x0b42549eu, 0x01875d87u, 0xcd2d5d19u, 0x43a25afau, 0x8f085a64u,
- 0x562848c8u, 0x9a824856u, 0x140d4fb5u, 0xd8a74f2bu, 0xd2624632u, 0x1ec846acu, 0x9047414fu, 0x5ced41d1u,
- 0x299dc2edu, 0xe537c273u, 0x6bb8c590u, 0xa712c50eu, 0xadd7cc17u, 0x617dcc89u, 0xeff2cb6au, 0x2358cbf4u,
- 0xfa78d958u, 0x36d2d9c6u, 0xb85dde25u, 0x74f7debbu, 0x7e32d7a2u, 0xb298d73cu, 0x3c17d0dfu, 0xf0bdd041u,
- 0x5526f3c6u, 0x998cf358u, 0x1703f4bbu, 0xdba9f425u, 0xd16cfd3cu, 0x1dc6fda2u, 0x9349fa41u, 0x5fe3fadfu,
- 0x86c3e873u, 0x4a69e8edu, 0xc4e6ef0eu, 0x084cef90u, 0x0289e689u, 0xce23e617u, 0x40ace1f4u, 0x8c06e16au,
- 0xd0eba0bbu, 0x1c41a025u, 0x92cea7c6u, 0x5e64a758u, 0x54a1ae41u, 0x980baedfu, 0x1684a93cu, 0xda2ea9a2u,
- 0x030ebb0eu, 0xcfa4bb90u, 0x412bbc73u, 0x8d81bcedu, 0x8744b5f4u, 0x4beeb56au, 0xc561b289u, 0x09cbb217u,
- 0xac509190u, 0x60fa910eu, 0xee7596edu, 0x22df9673u, 0x281a9f6au, 0xe4b09ff4u, 0x6a3f9817u, 0xa6959889u,
- 0x7fb58a25u, 0xb31f8abbu, 0x3d908d58u, 0xf13a8dc6u, 0xfbff84dfu, 0x37558441u, 0xb9da83a2u, 0x7570833cu,
- 0x533b85dau, 0x9f918544u, 0x111e82a7u, 0xddb48239u, 0xd7718b20u, 0x1bdb8bbeu, 0x95548c5du, 0x59fe8cc3u,
- 0x80de9e6fu, 0x4c749ef1u, 0xc2fb9912u, 0x0e51998cu, 0x04949095u, 0xc83e900bu, 0x46b197e8u, 0x8a1b9776u,
- 0x2f80b4f1u, 0xe32ab46fu, 0x6da5b38cu, 0xa10fb312u, 0xabcaba0bu, 0x6760ba95u, 0xe9efbd76u, 0x2545bde8u,
- 0xfc65af44u, 0x30cfafdau, 0xbe40a839u, 0x72eaa8a7u, 0x782fa1beu, 0xb485a120u, 0x3a0aa6c3u, 0xf6a0a65du,
- 0xaa4de78cu, 0x66e7e712u, 0xe868e0f1u, 0x24c2e06fu, 0x2e07e976u, 0xe2ade9e8u, 0x6c22ee0bu, 0xa088ee95u,
- 0x79a8fc39u, 0xb502fca7u, 0x3b8dfb44u, 0xf727fbdau, 0xfde2f2c3u, 0x3148f25du, 0xbfc7f5beu, 0x736df520u,
- 0xd6f6d6a7u, 0x1a5cd639u, 0x94d3d1dau, 0x5879d144u, 0x52bcd85du, 0x9e16d8c3u, 0x1099df20u, 0xdc33dfbeu,
- 0x0513cd12u, 0xc9b9cd8cu, 0x4736ca6fu, 0x8b9ccaf1u, 0x8159c3e8u, 0x4df3c376u, 0xc37cc495u, 0x0fd6c40bu,
- 0x7aa64737u, 0xb60c47a9u, 0x3883404au, 0xf42940d4u, 0xfeec49cdu, 0x32464953u, 0xbcc94eb0u, 0x70634e2eu,
- 0xa9435c82u, 0x65e95c1cu, 0xeb665bffu, 0x27cc5b61u, 0x2d095278u, 0xe1a352e6u, 0x6f2c5505u, 0xa386559bu,
- 0x061d761cu, 0xcab77682u, 0x44387161u, 0x889271ffu, 0x825778e6u, 0x4efd7878u, 0xc0727f9bu, 0x0cd87f05u,
- 0xd5f86da9u, 0x19526d37u, 0x97dd6ad4u, 0x5b776a4au, 0x51b26353u, 0x9d1863cdu, 0x1397642eu, 0xdf3d64b0u,
- 0x83d02561u, 0x4f7a25ffu, 0xc1f5221cu, 0x0d5f2282u, 0x079a2b9bu, 0xcb302b05u, 0x45bf2ce6u, 0x89152c78u,
- 0x50353ed4u, 0x9c9f3e4au, 0x121039a9u, 0xdeba3937u, 0xd47f302eu, 0x18d530b0u, 0x965a3753u, 0x5af037cdu,
- 0xff6b144au, 0x33c114d4u, 0xbd4e1337u, 0x71e413a9u, 0x7b211ab0u, 0xb78b1a2eu, 0x39041dcdu, 0xf5ae1d53u,
- 0x2c8e0fffu, 0xe0240f61u, 0x6eab0882u, 0xa201081cu, 0xa8c40105u, 0x646e019bu, 0xeae10678u, 0x264b06e6u
-};
-
-/* Computes the cyclic redundancy check as used by PNG chunks*/
-unsigned lodepng_crc32(const unsigned char* data, size_t length) {
- /*Using the Slicing by Eight algorithm*/
- unsigned r = 0xffffffffu;
- while(length >= 8) {
- r = lodepng_crc32_table7[(data[0] ^ (r & 0xffu))] ^
- lodepng_crc32_table6[(data[1] ^ ((r >> 8) & 0xffu))] ^
- lodepng_crc32_table5[(data[2] ^ ((r >> 16) & 0xffu))] ^
- lodepng_crc32_table4[(data[3] ^ ((r >> 24) & 0xffu))] ^
- lodepng_crc32_table3[data[4]] ^
- lodepng_crc32_table2[data[5]] ^
- lodepng_crc32_table1[data[6]] ^
- lodepng_crc32_table0[data[7]];
- data += 8;
- length -= 8;
- }
- while(length--) {
- r = lodepng_crc32_table0[(r ^ *data++) & 0xffu] ^ (r >> 8);
- }
- return r ^ 0xffffffffu;
-}
-#else /* LODEPNG_COMPILE_CRC */
-/*in this case, the function is only declared here, and must be defined externally
-so that it will be linked in.
-
-Example implementation that uses a much smaller lookup table for memory constrained cases:
-
-unsigned lodepng_crc32(const unsigned char* data, size_t length) {
- unsigned r = 0xffffffffu;
- static const unsigned table[16] = {
- 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
- 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
- };
- while(length--) {
- r = table[(r ^ *data) & 0xf] ^ (r >> 4);
- r = table[(r ^ (*data >> 4)) & 0xf] ^ (r >> 4);
- data++;
- }
- return r ^ 0xffffffffu;
-}
-*/
-unsigned lodepng_crc32(const unsigned char* data, size_t length);
-#endif /* LODEPNG_COMPILE_CRC */
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Reading and writing PNG color channel bits / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/* The color channel bits of less-than-8-bit pixels are read with the MSB of bytes first,
-so LodePNGBitWriter and LodePNGBitReader can't be used for those. */
-
-static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) {
- unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1);
- ++(*bitpointer);
- return result;
-}
-
-/* TODO: make this faster */
-static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) {
- unsigned result = 0;
- size_t i;
- for(i = 0 ; i < nbits; ++i) {
- result <<= 1u;
- result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream);
- }
- return result;
-}
-
-static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) {
- /*the current bit in bitstream may be 0 or 1 for this to work*/
- if(bit == 0) bitstream[(*bitpointer) >> 3u] &= (unsigned char)(~(1u << (7u - ((*bitpointer) & 7u))));
- else bitstream[(*bitpointer) >> 3u] |= (1u << (7u - ((*bitpointer) & 7u)));
- ++(*bitpointer);
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / PNG chunks / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-unsigned lodepng_chunk_length(const unsigned char* chunk) {
- return lodepng_read32bitInt(chunk);
-}
-
-void lodepng_chunk_type(char type[5], const unsigned char* chunk) {
- unsigned i;
- for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i];
- type[4] = 0; /*null termination char*/
-}
-
-unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) {
- if(lodepng_strlen(type) != 4) return 0;
- return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]);
-}
-
-unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) {
- return((chunk[4] & 32) != 0);
-}
-
-unsigned char lodepng_chunk_private(const unsigned char* chunk) {
- return((chunk[6] & 32) != 0);
-}
-
-unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk) {
- return((chunk[7] & 32) != 0);
-}
-
-unsigned char* lodepng_chunk_data(unsigned char* chunk) {
- return &chunk[8];
-}
-
-const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) {
- return &chunk[8];
-}
-
-unsigned lodepng_chunk_check_crc(const unsigned char* chunk) {
- unsigned length = lodepng_chunk_length(chunk);
- unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]);
- /*the CRC is taken of the data and the 4 chunk type letters, not the length*/
- unsigned checksum = lodepng_crc32(&chunk[4], length + 4);
- if(CRC != checksum) return 1;
- else return 0;
-}
-
-void lodepng_chunk_generate_crc(unsigned char* chunk) {
- unsigned length = lodepng_chunk_length(chunk);
- unsigned CRC = lodepng_crc32(&chunk[4], length + 4);
- lodepng_set32bitInt(chunk + 8 + length, CRC);
-}
-
-unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end) {
- size_t available_size = (size_t)(end - chunk);
- if(chunk >= end || available_size < 12) return end; /*too small to contain a chunk*/
- if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47
- && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) {
- /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */
- return chunk + 8;
- } else {
- size_t total_chunk_length;
- if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end;
- if(total_chunk_length > available_size) return end; /*outside of range*/
- return chunk + total_chunk_length;
- }
-}
-
-const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end) {
- size_t available_size = (size_t)(end - chunk);
- if(chunk >= end || available_size < 12) return end; /*too small to contain a chunk*/
- if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47
- && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) {
- /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */
- return chunk + 8;
- } else {
- size_t total_chunk_length;
- if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end;
- if(total_chunk_length > available_size) return end; /*outside of range*/
- return chunk + total_chunk_length;
- }
-}
-
-unsigned char* lodepng_chunk_find(unsigned char* chunk, unsigned char* end, const char type[5]) {
- for(;;) {
- if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */
- if(lodepng_chunk_type_equals(chunk, type)) return chunk;
- chunk = lodepng_chunk_next(chunk, end);
- }
-}
-
-const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]) {
- for(;;) {
- if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */
- if(lodepng_chunk_type_equals(chunk, type)) return chunk;
- chunk = lodepng_chunk_next_const(chunk, end);
- }
-}
-
-unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk) {
- unsigned i;
- size_t total_chunk_length, new_length;
- unsigned char *chunk_start, *new_buffer;
-
- if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return 77;
- if(lodepng_addofl(*outsize, total_chunk_length, &new_length)) return 77;
-
- new_buffer = (unsigned char*)lodepng_realloc(*out, new_length);
- if(!new_buffer) return 83; /*alloc fail*/
- (*out) = new_buffer;
- (*outsize) = new_length;
- chunk_start = &(*out)[new_length - total_chunk_length];
-
- for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i];
-
- return 0;
-}
-
-/*Sets length and name and allocates the space for data and crc but does not
-set data or crc yet. Returns the start of the chunk in chunk. The start of
-the data is at chunk + 8. To finalize chunk, add the data, then use
-lodepng_chunk_generate_crc */
-static unsigned lodepng_chunk_init(unsigned char** chunk,
- ucvector* out,
- unsigned length, const char* type) {
- size_t new_length = out->size;
- if(lodepng_addofl(new_length, length, &new_length)) return 77;
- if(lodepng_addofl(new_length, 12, &new_length)) return 77;
- if(!ucvector_resize(out, new_length)) return 83; /*alloc fail*/
- *chunk = out->data + new_length - length - 12u;
-
- /*1: length*/
- lodepng_set32bitInt(*chunk, length);
-
- /*2: chunk name (4 letters)*/
- lodepng_memcpy(*chunk + 4, type, 4);
-
- return 0;
-}
-
-/* like lodepng_chunk_create but with custom allocsize */
-static unsigned lodepng_chunk_createv(ucvector* out,
- unsigned length, const char* type, const unsigned char* data) {
- unsigned char* chunk;
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, length, type));
-
- /*3: the data*/
- lodepng_memcpy(chunk + 8, data, length);
-
- /*4: CRC (of the chunkname characters and the data)*/
- lodepng_chunk_generate_crc(chunk);
-
- return 0;
-}
-
-unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize,
- unsigned length, const char* type, const unsigned char* data) {
- ucvector v = ucvector_init(*out, *outsize);
- unsigned error = lodepng_chunk_createv(&v, length, type, data);
- *out = v.data;
- *outsize = v.size;
- return error;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / Color types, channels, bits / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*checks if the colortype is valid and the bitdepth bd is allowed for this colortype.
-Return value is a LodePNG error code.*/
-static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) {
- switch(colortype) {
- case LCT_GREY: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break;
- case LCT_RGB: if(!( bd == 8 || bd == 16)) return 37; break;
- case LCT_PALETTE: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break;
- case LCT_GREY_ALPHA: if(!( bd == 8 || bd == 16)) return 37; break;
- case LCT_RGBA: if(!( bd == 8 || bd == 16)) return 37; break;
- case LCT_MAX_OCTET_VALUE: return 31; /* invalid color type */
- default: return 31; /* invalid color type */
- }
- return 0; /*allowed color type / bits combination*/
-}
-
-static unsigned getNumColorChannels(LodePNGColorType colortype) {
- switch(colortype) {
- case LCT_GREY: return 1;
- case LCT_RGB: return 3;
- case LCT_PALETTE: return 1;
- case LCT_GREY_ALPHA: return 2;
- case LCT_RGBA: return 4;
- case LCT_MAX_OCTET_VALUE: return 0; /* invalid color type */
- default: return 0; /*invalid color type*/
- }
-}
-
-static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) {
- /*bits per pixel is amount of channels * bits per channel*/
- return getNumColorChannels(colortype) * bitdepth;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-void lodepng_color_mode_init(LodePNGColorMode* info) {
- info->key_defined = 0;
- info->key_r = info->key_g = info->key_b = 0;
- info->colortype = LCT_RGBA;
- info->bitdepth = 8;
- info->palette = 0;
- info->palettesize = 0;
-}
-
-/*allocates palette memory if needed, and initializes all colors to black*/
-static void lodepng_color_mode_alloc_palette(LodePNGColorMode* info) {
- size_t i;
- /*if the palette is already allocated, it will have size 1024 so no reallocation needed in that case*/
- /*the palette must have room for up to 256 colors with 4 bytes each.*/
- if(!info->palette) info->palette = (unsigned char*)lodepng_malloc(1024);
- if(!info->palette) return; /*alloc fail*/
- for(i = 0; i != 256; ++i) {
- /*Initialize all unused colors with black, the value used for invalid palette indices.
- This is an error according to the PNG spec, but common PNG decoders make it black instead.
- That makes color conversion slightly faster due to no error handling needed.*/
- info->palette[i * 4 + 0] = 0;
- info->palette[i * 4 + 1] = 0;
- info->palette[i * 4 + 2] = 0;
- info->palette[i * 4 + 3] = 255;
- }
-}
-
-void lodepng_color_mode_cleanup(LodePNGColorMode* info) {
- lodepng_palette_clear(info);
-}
-
-unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) {
- lodepng_color_mode_cleanup(dest);
- lodepng_memcpy(dest, source, sizeof(LodePNGColorMode));
- if(source->palette) {
- dest->palette = (unsigned char*)lodepng_malloc(1024);
- if(!dest->palette && source->palettesize) return 83; /*alloc fail*/
- lodepng_memcpy(dest->palette, source->palette, source->palettesize * 4);
- }
- return 0;
-}
-
-LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth) {
- LodePNGColorMode result;
- lodepng_color_mode_init(&result);
- result.colortype = colortype;
- result.bitdepth = bitdepth;
- return result;
-}
-
-static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) {
- size_t i;
- if(a->colortype != b->colortype) return 0;
- if(a->bitdepth != b->bitdepth) return 0;
- if(a->key_defined != b->key_defined) return 0;
- if(a->key_defined) {
- if(a->key_r != b->key_r) return 0;
- if(a->key_g != b->key_g) return 0;
- if(a->key_b != b->key_b) return 0;
- }
- if(a->palettesize != b->palettesize) return 0;
- for(i = 0; i != a->palettesize * 4; ++i) {
- if(a->palette[i] != b->palette[i]) return 0;
- }
- return 1;
-}
-
-void lodepng_palette_clear(LodePNGColorMode* info) {
- if(info->palette) lodepng_free(info->palette);
- info->palette = 0;
- info->palettesize = 0;
-}
-
-unsigned lodepng_palette_add(LodePNGColorMode* info,
- unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
- if(!info->palette) /*allocate palette if empty*/ {
- lodepng_color_mode_alloc_palette(info);
- if(!info->palette) return 83; /*alloc fail*/
- }
- if(info->palettesize >= 256) {
- return 108; /*too many palette values*/
- }
- info->palette[4 * info->palettesize + 0] = r;
- info->palette[4 * info->palettesize + 1] = g;
- info->palette[4 * info->palettesize + 2] = b;
- info->palette[4 * info->palettesize + 3] = a;
- ++info->palettesize;
- return 0;
-}
-
-/*calculate bits per pixel out of colortype and bitdepth*/
-unsigned lodepng_get_bpp(const LodePNGColorMode* info) {
- return lodepng_get_bpp_lct(info->colortype, info->bitdepth);
-}
-
-unsigned lodepng_get_channels(const LodePNGColorMode* info) {
- return getNumColorChannels(info->colortype);
-}
-
-unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info) {
- return info->colortype == LCT_GREY || info->colortype == LCT_GREY_ALPHA;
-}
-
-unsigned lodepng_is_alpha_type(const LodePNGColorMode* info) {
- return (info->colortype & 4) != 0; /*4 or 6*/
-}
-
-unsigned lodepng_is_palette_type(const LodePNGColorMode* info) {
- return info->colortype == LCT_PALETTE;
-}
-
-unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) {
- size_t i;
- for(i = 0; i != info->palettesize; ++i) {
- if(info->palette[i * 4 + 3] < 255) return 1;
- }
- return 0;
-}
-
-unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) {
- return info->key_defined
- || lodepng_is_alpha_type(info)
- || lodepng_has_palette_alpha(info);
-}
-
-static size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) {
- size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth);
- size_t n = (size_t)w * (size_t)h;
- return ((n / 8u) * bpp) + ((n & 7u) * bpp + 7u) / 8u;
-}
-
-size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) {
- return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth);
-}
-
-
-#ifdef LODEPNG_COMPILE_PNG
-
-/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer,
-and in addition has one extra byte per line: the filter byte. So this gives a larger
-result than lodepng_get_raw_size. Set h to 1 to get the size of 1 row including filter byte. */
-static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, unsigned bpp) {
- /* + 1 for the filter byte, and possibly plus padding bits per line. */
- /* Ignoring casts, the expression is equal to (w * bpp + 7) / 8 + 1, but avoids overflow of w * bpp */
- size_t line = ((size_t)(w / 8u) * bpp) + 1u + ((w & 7u) * bpp + 7u) / 8u;
- return (size_t)h * line;
-}
-
-#ifdef LODEPNG_COMPILE_DECODER
-/*Safely checks whether size_t overflow can be caused due to amount of pixels.
-This check is overcautious rather than precise. If this check indicates no overflow,
-you can safely compute in a size_t (but not an unsigned):
--(size_t)w * (size_t)h * 8
--amount of bytes in IDAT (including filter, padding and Adam7 bytes)
--amount of bytes in raw color model
-Returns 1 if overflow possible, 0 if not.
-*/
-static int lodepng_pixel_overflow(unsigned w, unsigned h,
- const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor) {
- size_t bpp = LODEPNG_MAX(lodepng_get_bpp(pngcolor), lodepng_get_bpp(rawcolor));
- size_t numpixels, total;
- size_t line; /* bytes per line in worst case */
-
- if(lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1;
- if(lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */
-
- /* Bytes per scanline with the expression "(w / 8u) * bpp) + ((w & 7u) * bpp + 7u) / 8u" */
- if(lodepng_mulofl((size_t)(w / 8u), bpp, &line)) return 1;
- if(lodepng_addofl(line, ((w & 7u) * bpp + 7u) / 8u, &line)) return 1;
-
- if(lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */
- if(lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */
-
- return 0; /* no overflow */
-}
-#endif /*LODEPNG_COMPILE_DECODER*/
-#endif /*LODEPNG_COMPILE_PNG*/
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-
-static void LodePNGUnknownChunks_init(LodePNGInfo* info) {
- unsigned i;
- for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0;
- for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0;
-}
-
-static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) {
- unsigned i;
- for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]);
-}
-
-static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) {
- unsigned i;
-
- LodePNGUnknownChunks_cleanup(dest);
-
- for(i = 0; i != 3; ++i) {
- size_t j;
- dest->unknown_chunks_size[i] = src->unknown_chunks_size[i];
- dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]);
- if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/
- for(j = 0; j < src->unknown_chunks_size[i]; ++j) {
- dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j];
- }
- }
-
- return 0;
-}
-
-/******************************************************************************/
-
-static void LodePNGText_init(LodePNGInfo* info) {
- info->text_num = 0;
- info->text_keys = NULL;
- info->text_strings = NULL;
-}
-
-static void LodePNGText_cleanup(LodePNGInfo* info) {
- size_t i;
- for(i = 0; i != info->text_num; ++i) {
- string_cleanup(&info->text_keys[i]);
- string_cleanup(&info->text_strings[i]);
- }
- lodepng_free(info->text_keys);
- lodepng_free(info->text_strings);
-}
-
-static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
- size_t i = 0;
- dest->text_keys = NULL;
- dest->text_strings = NULL;
- dest->text_num = 0;
- for(i = 0; i != source->text_num; ++i) {
- CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i]));
- }
- return 0;
-}
-
-static unsigned lodepng_add_text_sized(LodePNGInfo* info, const char* key, const char* str, size_t size) {
- char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1)));
- char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1)));
-
- if(new_keys) info->text_keys = new_keys;
- if(new_strings) info->text_strings = new_strings;
-
- if(!new_keys || !new_strings) return 83; /*alloc fail*/
-
- ++info->text_num;
- info->text_keys[info->text_num - 1] = alloc_string(key);
- info->text_strings[info->text_num - 1] = alloc_string_sized(str, size);
- if(!info->text_keys[info->text_num - 1] || !info->text_strings[info->text_num - 1]) return 83; /*alloc fail*/
-
- return 0;
-}
-
-unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) {
- return lodepng_add_text_sized(info, key, str, lodepng_strlen(str));
-}
-
-void lodepng_clear_text(LodePNGInfo* info) {
- LodePNGText_cleanup(info);
-}
-
-/******************************************************************************/
-
-static void LodePNGIText_init(LodePNGInfo* info) {
- info->itext_num = 0;
- info->itext_keys = NULL;
- info->itext_langtags = NULL;
- info->itext_transkeys = NULL;
- info->itext_strings = NULL;
-}
-
-static void LodePNGIText_cleanup(LodePNGInfo* info) {
- size_t i;
- for(i = 0; i != info->itext_num; ++i) {
- string_cleanup(&info->itext_keys[i]);
- string_cleanup(&info->itext_langtags[i]);
- string_cleanup(&info->itext_transkeys[i]);
- string_cleanup(&info->itext_strings[i]);
- }
- lodepng_free(info->itext_keys);
- lodepng_free(info->itext_langtags);
- lodepng_free(info->itext_transkeys);
- lodepng_free(info->itext_strings);
-}
-
-static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
- size_t i = 0;
- dest->itext_keys = NULL;
- dest->itext_langtags = NULL;
- dest->itext_transkeys = NULL;
- dest->itext_strings = NULL;
- dest->itext_num = 0;
- for(i = 0; i != source->itext_num; ++i) {
- CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i],
- source->itext_transkeys[i], source->itext_strings[i]));
- }
- return 0;
-}
-
-void lodepng_clear_itext(LodePNGInfo* info) {
- LodePNGIText_cleanup(info);
-}
-
-static unsigned lodepng_add_itext_sized(LodePNGInfo* info, const char* key, const char* langtag,
- const char* transkey, const char* str, size_t size) {
- char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1)));
- char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1)));
- char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1)));
- char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1)));
-
- if(new_keys) info->itext_keys = new_keys;
- if(new_langtags) info->itext_langtags = new_langtags;
- if(new_transkeys) info->itext_transkeys = new_transkeys;
- if(new_strings) info->itext_strings = new_strings;
-
- if(!new_keys || !new_langtags || !new_transkeys || !new_strings) return 83; /*alloc fail*/
-
- ++info->itext_num;
-
- info->itext_keys[info->itext_num - 1] = alloc_string(key);
- info->itext_langtags[info->itext_num - 1] = alloc_string(langtag);
- info->itext_transkeys[info->itext_num - 1] = alloc_string(transkey);
- info->itext_strings[info->itext_num - 1] = alloc_string_sized(str, size);
-
- return 0;
-}
-
-unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag,
- const char* transkey, const char* str) {
- return lodepng_add_itext_sized(info, key, langtag, transkey, str, lodepng_strlen(str));
-}
-
-/* same as set but does not delete */
-static unsigned lodepng_assign_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) {
- if(profile_size == 0) return 100; /*invalid ICC profile size*/
-
- info->iccp_name = alloc_string(name);
- info->iccp_profile = (unsigned char*)lodepng_malloc(profile_size);
-
- if(!info->iccp_name || !info->iccp_profile) return 83; /*alloc fail*/
-
- lodepng_memcpy(info->iccp_profile, profile, profile_size);
- info->iccp_profile_size = profile_size;
-
- return 0; /*ok*/
-}
-
-unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) {
- if(info->iccp_name) lodepng_clear_icc(info);
- info->iccp_defined = 1;
-
- return lodepng_assign_icc(info, name, profile, profile_size);
-}
-
-void lodepng_clear_icc(LodePNGInfo* info) {
- string_cleanup(&info->iccp_name);
- lodepng_free(info->iccp_profile);
- info->iccp_profile = NULL;
- info->iccp_profile_size = 0;
- info->iccp_defined = 0;
-}
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-void lodepng_info_init(LodePNGInfo* info) {
- lodepng_color_mode_init(&info->color);
- info->interlace_method = 0;
- info->compression_method = 0;
- info->filter_method = 0;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- info->background_defined = 0;
- info->background_r = info->background_g = info->background_b = 0;
-
- LodePNGText_init(info);
- LodePNGIText_init(info);
-
- info->time_defined = 0;
- info->phys_defined = 0;
-
- info->gama_defined = 0;
- info->chrm_defined = 0;
- info->srgb_defined = 0;
- info->iccp_defined = 0;
- info->iccp_name = NULL;
- info->iccp_profile = NULL;
-
- info->sbit_defined = 0;
- info->sbit_r = info->sbit_g = info->sbit_b = info->sbit_a = 0;
-
- LodePNGUnknownChunks_init(info);
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-}
-
-void lodepng_info_cleanup(LodePNGInfo* info) {
- lodepng_color_mode_cleanup(&info->color);
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- LodePNGText_cleanup(info);
- LodePNGIText_cleanup(info);
-
- lodepng_clear_icc(info);
-
- LodePNGUnknownChunks_cleanup(info);
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-}
-
-unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) {
- lodepng_info_cleanup(dest);
- lodepng_memcpy(dest, source, sizeof(LodePNGInfo));
- lodepng_color_mode_init(&dest->color);
- CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color));
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- CERROR_TRY_RETURN(LodePNGText_copy(dest, source));
- CERROR_TRY_RETURN(LodePNGIText_copy(dest, source));
- if(source->iccp_defined) {
- CERROR_TRY_RETURN(lodepng_assign_icc(dest, source->iccp_name, source->iccp_profile, source->iccp_profile_size));
- }
-
- LodePNGUnknownChunks_init(dest);
- CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source));
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
- return 0;
-}
-
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/
-static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) {
- unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/
- /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/
- unsigned p = index & m;
- in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/
- in = in << (bits * (m - p));
- if(p == 0) out[index * bits / 8u] = in;
- else out[index * bits / 8u] |= in;
-}
-
-typedef struct ColorTree ColorTree;
-
-/*
-One node of a color tree
-This is the data structure used to count the number of unique colors and to get a palette
-index for a color. It's like an octree, but because the alpha channel is used too, each
-node has 16 instead of 8 children.
-*/
-struct ColorTree {
- ColorTree* children[16]; /*up to 16 pointers to ColorTree of next level*/
- int index; /*the payload. Only has a meaningful value if this is in the last level*/
-};
-
-static void color_tree_init(ColorTree* tree) {
- lodepng_memset(tree->children, 0, 16 * sizeof(*tree->children));
- tree->index = -1;
-}
-
-static void color_tree_cleanup(ColorTree* tree) {
- int i;
- for(i = 0; i != 16; ++i) {
- if(tree->children[i]) {
- color_tree_cleanup(tree->children[i]);
- lodepng_free(tree->children[i]);
- }
- }
-}
-
-/*returns -1 if color not present, its index otherwise*/
-static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
- int bit = 0;
- for(bit = 0; bit < 8; ++bit) {
- int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1);
- if(!tree->children[i]) return -1;
- else tree = tree->children[i];
- }
- return tree ? tree->index : -1;
-}
-
-#ifdef LODEPNG_COMPILE_ENCODER
-static int color_tree_has(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
- return color_tree_get(tree, r, g, b, a) >= 0;
-}
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-/*color is not allowed to already exist.
-Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")
-Returns error code, or 0 if ok*/
-static unsigned color_tree_add(ColorTree* tree,
- unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) {
- int bit;
- for(bit = 0; bit < 8; ++bit) {
- int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1);
- if(!tree->children[i]) {
- tree->children[i] = (ColorTree*)lodepng_malloc(sizeof(ColorTree));
- if(!tree->children[i]) return 83; /*alloc fail*/
- color_tree_init(tree->children[i]);
- }
- tree = tree->children[i];
- }
- tree->index = (int)index;
- return 0;
-}
-
-/*put a pixel, given its RGBA color, into image of any color type*/
-static unsigned rgba8ToPixel(unsigned char* out, size_t i,
- const LodePNGColorMode* mode, ColorTree* tree /*for palette*/,
- unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
- if(mode->colortype == LCT_GREY) {
- unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/
- if(mode->bitdepth == 8) out[i] = gray;
- else if(mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = gray;
- else {
- /*take the most significant bits of gray*/
- gray = ((unsigned)gray >> (8u - mode->bitdepth)) & ((1u << mode->bitdepth) - 1u);
- addColorBits(out, i, mode->bitdepth, gray);
- }
- } else if(mode->colortype == LCT_RGB) {
- if(mode->bitdepth == 8) {
- out[i * 3 + 0] = r;
- out[i * 3 + 1] = g;
- out[i * 3 + 2] = b;
- } else {
- out[i * 6 + 0] = out[i * 6 + 1] = r;
- out[i * 6 + 2] = out[i * 6 + 3] = g;
- out[i * 6 + 4] = out[i * 6 + 5] = b;
- }
- } else if(mode->colortype == LCT_PALETTE) {
- int index = color_tree_get(tree, r, g, b, a);
- if(index < 0) return 82; /*color not in palette*/
- if(mode->bitdepth == 8) out[i] = index;
- else addColorBits(out, i, mode->bitdepth, (unsigned)index);
- } else if(mode->colortype == LCT_GREY_ALPHA) {
- unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/
- if(mode->bitdepth == 8) {
- out[i * 2 + 0] = gray;
- out[i * 2 + 1] = a;
- } else if(mode->bitdepth == 16) {
- out[i * 4 + 0] = out[i * 4 + 1] = gray;
- out[i * 4 + 2] = out[i * 4 + 3] = a;
- }
- } else if(mode->colortype == LCT_RGBA) {
- if(mode->bitdepth == 8) {
- out[i * 4 + 0] = r;
- out[i * 4 + 1] = g;
- out[i * 4 + 2] = b;
- out[i * 4 + 3] = a;
- } else {
- out[i * 8 + 0] = out[i * 8 + 1] = r;
- out[i * 8 + 2] = out[i * 8 + 3] = g;
- out[i * 8 + 4] = out[i * 8 + 5] = b;
- out[i * 8 + 6] = out[i * 8 + 7] = a;
- }
- }
-
- return 0; /*no error*/
-}
-
-/*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/
-static void rgba16ToPixel(unsigned char* out, size_t i,
- const LodePNGColorMode* mode,
- unsigned short r, unsigned short g, unsigned short b, unsigned short a) {
- if(mode->colortype == LCT_GREY) {
- unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/
- out[i * 2 + 0] = (gray >> 8) & 255;
- out[i * 2 + 1] = gray & 255;
- } else if(mode->colortype == LCT_RGB) {
- out[i * 6 + 0] = (r >> 8) & 255;
- out[i * 6 + 1] = r & 255;
- out[i * 6 + 2] = (g >> 8) & 255;
- out[i * 6 + 3] = g & 255;
- out[i * 6 + 4] = (b >> 8) & 255;
- out[i * 6 + 5] = b & 255;
- } else if(mode->colortype == LCT_GREY_ALPHA) {
- unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/
- out[i * 4 + 0] = (gray >> 8) & 255;
- out[i * 4 + 1] = gray & 255;
- out[i * 4 + 2] = (a >> 8) & 255;
- out[i * 4 + 3] = a & 255;
- } else if(mode->colortype == LCT_RGBA) {
- out[i * 8 + 0] = (r >> 8) & 255;
- out[i * 8 + 1] = r & 255;
- out[i * 8 + 2] = (g >> 8) & 255;
- out[i * 8 + 3] = g & 255;
- out[i * 8 + 4] = (b >> 8) & 255;
- out[i * 8 + 5] = b & 255;
- out[i * 8 + 6] = (a >> 8) & 255;
- out[i * 8 + 7] = a & 255;
- }
-}
-
-/*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/
-static void getPixelColorRGBA8(unsigned char* r, unsigned char* g,
- unsigned char* b, unsigned char* a,
- const unsigned char* in, size_t i,
- const LodePNGColorMode* mode) {
- if(mode->colortype == LCT_GREY) {
- if(mode->bitdepth == 8) {
- *r = *g = *b = in[i];
- if(mode->key_defined && *r == mode->key_r) *a = 0;
- else *a = 255;
- } else if(mode->bitdepth == 16) {
- *r = *g = *b = in[i * 2 + 0];
- if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0;
- else *a = 255;
- } else {
- unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/
- size_t j = i * mode->bitdepth;
- unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
- *r = *g = *b = (value * 255) / highest;
- if(mode->key_defined && value == mode->key_r) *a = 0;
- else *a = 255;
- }
- } else if(mode->colortype == LCT_RGB) {
- if(mode->bitdepth == 8) {
- *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2];
- if(mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0;
- else *a = 255;
- } else {
- *r = in[i * 6 + 0];
- *g = in[i * 6 + 2];
- *b = in[i * 6 + 4];
- if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
- && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
- && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0;
- else *a = 255;
- }
- } else if(mode->colortype == LCT_PALETTE) {
- unsigned index;
- if(mode->bitdepth == 8) index = in[i];
- else {
- size_t j = i * mode->bitdepth;
- index = readBitsFromReversedStream(&j, in, mode->bitdepth);
- }
- /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/
- *r = mode->palette[index * 4 + 0];
- *g = mode->palette[index * 4 + 1];
- *b = mode->palette[index * 4 + 2];
- *a = mode->palette[index * 4 + 3];
- } else if(mode->colortype == LCT_GREY_ALPHA) {
- if(mode->bitdepth == 8) {
- *r = *g = *b = in[i * 2 + 0];
- *a = in[i * 2 + 1];
- } else {
- *r = *g = *b = in[i * 4 + 0];
- *a = in[i * 4 + 2];
- }
- } else if(mode->colortype == LCT_RGBA) {
- if(mode->bitdepth == 8) {
- *r = in[i * 4 + 0];
- *g = in[i * 4 + 1];
- *b = in[i * 4 + 2];
- *a = in[i * 4 + 3];
- } else {
- *r = in[i * 8 + 0];
- *g = in[i * 8 + 2];
- *b = in[i * 8 + 4];
- *a = in[i * 8 + 6];
- }
- }
-}
-
-/*Similar to getPixelColorRGBA8, but with all the for loops inside of the color
-mode test cases, optimized to convert the colors much faster, when converting
-to the common case of RGBA with 8 bit per channel. buffer must be RGBA with
-enough memory.*/
-static void getPixelColorsRGBA8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels,
- const unsigned char* LODEPNG_RESTRICT in,
- const LodePNGColorMode* mode) {
- unsigned num_channels = 4;
- size_t i;
- if(mode->colortype == LCT_GREY) {
- if(mode->bitdepth == 8) {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- buffer[0] = buffer[1] = buffer[2] = in[i];
- buffer[3] = 255;
- }
- if(mode->key_defined) {
- buffer -= numpixels * num_channels;
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- if(buffer[0] == mode->key_r) buffer[3] = 0;
- }
- }
- } else if(mode->bitdepth == 16) {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- buffer[0] = buffer[1] = buffer[2] = in[i * 2];
- buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255;
- }
- } else {
- unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/
- size_t j = 0;
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
- buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest;
- buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255;
- }
- }
- } else if(mode->colortype == LCT_RGB) {
- if(mode->bitdepth == 8) {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- lodepng_memcpy(buffer, &in[i * 3], 3);
- buffer[3] = 255;
- }
- if(mode->key_defined) {
- buffer -= numpixels * num_channels;
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- if(buffer[0] == mode->key_r && buffer[1]== mode->key_g && buffer[2] == mode->key_b) buffer[3] = 0;
- }
- }
- } else {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- buffer[0] = in[i * 6 + 0];
- buffer[1] = in[i * 6 + 2];
- buffer[2] = in[i * 6 + 4];
- buffer[3] = mode->key_defined
- && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
- && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
- && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255;
- }
- }
- } else if(mode->colortype == LCT_PALETTE) {
- if(mode->bitdepth == 8) {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- unsigned index = in[i];
- /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/
- lodepng_memcpy(buffer, &mode->palette[index * 4], 4);
- }
- } else {
- size_t j = 0;
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth);
- /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/
- lodepng_memcpy(buffer, &mode->palette[index * 4], 4);
- }
- }
- } else if(mode->colortype == LCT_GREY_ALPHA) {
- if(mode->bitdepth == 8) {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0];
- buffer[3] = in[i * 2 + 1];
- }
- } else {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0];
- buffer[3] = in[i * 4 + 2];
- }
- }
- } else if(mode->colortype == LCT_RGBA) {
- if(mode->bitdepth == 8) {
- lodepng_memcpy(buffer, in, numpixels * 4);
- } else {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- buffer[0] = in[i * 8 + 0];
- buffer[1] = in[i * 8 + 2];
- buffer[2] = in[i * 8 + 4];
- buffer[3] = in[i * 8 + 6];
- }
- }
- }
-}
-
-/*Similar to getPixelColorsRGBA8, but with 3-channel RGB output.*/
-static void getPixelColorsRGB8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels,
- const unsigned char* LODEPNG_RESTRICT in,
- const LodePNGColorMode* mode) {
- const unsigned num_channels = 3;
- size_t i;
- if(mode->colortype == LCT_GREY) {
- if(mode->bitdepth == 8) {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- buffer[0] = buffer[1] = buffer[2] = in[i];
- }
- } else if(mode->bitdepth == 16) {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- buffer[0] = buffer[1] = buffer[2] = in[i * 2];
- }
- } else {
- unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/
- size_t j = 0;
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
- buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest;
- }
- }
- } else if(mode->colortype == LCT_RGB) {
- if(mode->bitdepth == 8) {
- lodepng_memcpy(buffer, in, numpixels * 3);
- } else {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- buffer[0] = in[i * 6 + 0];
- buffer[1] = in[i * 6 + 2];
- buffer[2] = in[i * 6 + 4];
- }
- }
- } else if(mode->colortype == LCT_PALETTE) {
- if(mode->bitdepth == 8) {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- unsigned index = in[i];
- /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/
- lodepng_memcpy(buffer, &mode->palette[index * 4], 3);
- }
- } else {
- size_t j = 0;
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth);
- /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/
- lodepng_memcpy(buffer, &mode->palette[index * 4], 3);
- }
- }
- } else if(mode->colortype == LCT_GREY_ALPHA) {
- if(mode->bitdepth == 8) {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0];
- }
- } else {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0];
- }
- }
- } else if(mode->colortype == LCT_RGBA) {
- if(mode->bitdepth == 8) {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- lodepng_memcpy(buffer, &in[i * 4], 3);
- }
- } else {
- for(i = 0; i != numpixels; ++i, buffer += num_channels) {
- buffer[0] = in[i * 8 + 0];
- buffer[1] = in[i * 8 + 2];
- buffer[2] = in[i * 8 + 4];
- }
- }
- }
-}
-
-/*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with
-given color type, but the given color type must be 16-bit itself.*/
-static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a,
- const unsigned char* in, size_t i, const LodePNGColorMode* mode) {
- if(mode->colortype == LCT_GREY) {
- *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1];
- if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0;
- else *a = 65535;
- } else if(mode->colortype == LCT_RGB) {
- *r = 256u * in[i * 6 + 0] + in[i * 6 + 1];
- *g = 256u * in[i * 6 + 2] + in[i * 6 + 3];
- *b = 256u * in[i * 6 + 4] + in[i * 6 + 5];
- if(mode->key_defined
- && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
- && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
- && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0;
- else *a = 65535;
- } else if(mode->colortype == LCT_GREY_ALPHA) {
- *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1];
- *a = 256u * in[i * 4 + 2] + in[i * 4 + 3];
- } else if(mode->colortype == LCT_RGBA) {
- *r = 256u * in[i * 8 + 0] + in[i * 8 + 1];
- *g = 256u * in[i * 8 + 2] + in[i * 8 + 3];
- *b = 256u * in[i * 8 + 4] + in[i * 8 + 5];
- *a = 256u * in[i * 8 + 6] + in[i * 8 + 7];
- }
-}
-
-unsigned lodepng_convert(unsigned char* out, const unsigned char* in,
- const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in,
- unsigned w, unsigned h) {
- size_t i;
- ColorTree tree;
- size_t numpixels = (size_t)w * (size_t)h;
- unsigned error = 0;
-
- if(mode_in->colortype == LCT_PALETTE && !mode_in->palette) {
- return 107; /* error: must provide palette if input mode is palette */
- }
-
- if(lodepng_color_mode_equal(mode_out, mode_in)) {
- size_t numbytes = lodepng_get_raw_size(w, h, mode_in);
- lodepng_memcpy(out, in, numbytes);
- return 0;
- }
-
- if(mode_out->colortype == LCT_PALETTE) {
- size_t palettesize = mode_out->palettesize;
- const unsigned char* palette = mode_out->palette;
- size_t palsize = (size_t)1u << mode_out->bitdepth;
- /*if the user specified output palette but did not give the values, assume
- they want the values of the input color type (assuming that one is palette).
- Note that we never create a new palette ourselves.*/
- if(palettesize == 0) {
- palettesize = mode_in->palettesize;
- palette = mode_in->palette;
- /*if the input was also palette with same bitdepth, then the color types are also
- equal, so copy literally. This to preserve the exact indices that were in the PNG
- even in case there are duplicate colors in the palette.*/
- if(mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) {
- size_t numbytes = lodepng_get_raw_size(w, h, mode_in);
- lodepng_memcpy(out, in, numbytes);
- return 0;
- }
- }
- if(palettesize < palsize) palsize = palettesize;
- color_tree_init(&tree);
- for(i = 0; i != palsize; ++i) {
- const unsigned char* p = &palette[i * 4];
- error = color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i);
- if(error) break;
- }
- }
-
- if(!error) {
- if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) {
- for(i = 0; i != numpixels; ++i) {
- unsigned short r = 0, g = 0, b = 0, a = 0;
- getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
- rgba16ToPixel(out, i, mode_out, r, g, b, a);
- }
- } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) {
- getPixelColorsRGBA8(out, numpixels, in, mode_in);
- } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) {
- getPixelColorsRGB8(out, numpixels, in, mode_in);
- } else {
- unsigned char r = 0, g = 0, b = 0, a = 0;
- for(i = 0; i != numpixels; ++i) {
- getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in);
- error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a);
- if(error) break;
- }
- }
- }
-
- if(mode_out->colortype == LCT_PALETTE) {
- color_tree_cleanup(&tree);
- }
-
- return error;
-}
-
-
-/* Converts a single rgb color without alpha from one type to another, color bits truncated to
-their bitdepth. In case of single channel (gray or palette), only the r channel is used. Slow
-function, do not use to process all pixels of an image. Alpha channel not supported on purpose:
-this is for bKGD, supporting alpha may prevent it from finding a color in the palette, from the
-specification it looks like bKGD should ignore the alpha values of the palette since it can use
-any palette index but doesn't have an alpha channel. Idem with ignoring color key. */
-unsigned lodepng_convert_rgb(
- unsigned* r_out, unsigned* g_out, unsigned* b_out,
- unsigned r_in, unsigned g_in, unsigned b_in,
- const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in) {
- unsigned r = 0, g = 0, b = 0;
- unsigned mul = 65535 / ((1u << mode_in->bitdepth) - 1u); /*65535, 21845, 4369, 257, 1*/
- unsigned shift = 16 - mode_out->bitdepth;
-
- if(mode_in->colortype == LCT_GREY || mode_in->colortype == LCT_GREY_ALPHA) {
- r = g = b = r_in * mul;
- } else if(mode_in->colortype == LCT_RGB || mode_in->colortype == LCT_RGBA) {
- r = r_in * mul;
- g = g_in * mul;
- b = b_in * mul;
- } else if(mode_in->colortype == LCT_PALETTE) {
- if(r_in >= mode_in->palettesize) return 82;
- r = mode_in->palette[r_in * 4 + 0] * 257u;
- g = mode_in->palette[r_in * 4 + 1] * 257u;
- b = mode_in->palette[r_in * 4 + 2] * 257u;
- } else {
- return 31;
- }
-
- /* now convert to output format */
- if(mode_out->colortype == LCT_GREY || mode_out->colortype == LCT_GREY_ALPHA) {
- *r_out = r >> shift ;
- } else if(mode_out->colortype == LCT_RGB || mode_out->colortype == LCT_RGBA) {
- *r_out = r >> shift ;
- *g_out = g >> shift ;
- *b_out = b >> shift ;
- } else if(mode_out->colortype == LCT_PALETTE) {
- unsigned i;
- /* a 16-bit color cannot be in the palette */
- if((r >> 8) != (r & 255) || (g >> 8) != (g & 255) || (b >> 8) != (b & 255)) return 82;
- for(i = 0; i < mode_out->palettesize; i++) {
- unsigned j = i * 4;
- if((r >> 8) == mode_out->palette[j + 0] && (g >> 8) == mode_out->palette[j + 1] &&
- (b >> 8) == mode_out->palette[j + 2]) {
- *r_out = i;
- return 0;
- }
- }
- return 82;
- } else {
- return 31;
- }
-
- return 0;
-}
-
-#ifdef LODEPNG_COMPILE_ENCODER
-
-void lodepng_color_stats_init(LodePNGColorStats* stats) {
- /*stats*/
- stats->colored = 0;
- stats->key = 0;
- stats->key_r = stats->key_g = stats->key_b = 0;
- stats->alpha = 0;
- stats->numcolors = 0;
- stats->bits = 1;
- stats->numpixels = 0;
- /*settings*/
- stats->allow_palette = 1;
- stats->allow_greyscale = 1;
-}
-
-/*function used for debug purposes with C++*/
-/*void printColorStats(LodePNGColorStats* p) {
- std::cout << "colored: " << (int)p->colored << ", ";
- std::cout << "key: " << (int)p->key << ", ";
- std::cout << "key_r: " << (int)p->key_r << ", ";
- std::cout << "key_g: " << (int)p->key_g << ", ";
- std::cout << "key_b: " << (int)p->key_b << ", ";
- std::cout << "alpha: " << (int)p->alpha << ", ";
- std::cout << "numcolors: " << (int)p->numcolors << ", ";
- std::cout << "bits: " << (int)p->bits << std::endl;
-}*/
-
-/*Returns how many bits needed to represent given value (max 8 bit)*/
-static unsigned getValueRequiredBits(unsigned char value) {
- if(value == 0 || value == 255) return 1;
- /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/
- if(value % 17 == 0) return value % 85 == 0 ? 2 : 4;
- return 8;
-}
-
-/*stats must already have been inited. */
-unsigned lodepng_compute_color_stats(LodePNGColorStats* stats,
- const unsigned char* in, unsigned w, unsigned h,
- const LodePNGColorMode* mode_in) {
- size_t i;
- ColorTree tree;
- size_t numpixels = (size_t)w * (size_t)h;
- unsigned error = 0;
-
- /* mark things as done already if it would be impossible to have a more expensive case */
- unsigned colored_done = lodepng_is_greyscale_type(mode_in) ? 1 : 0;
- unsigned alpha_done = lodepng_can_have_alpha(mode_in) ? 0 : 1;
- unsigned numcolors_done = 0;
- unsigned bpp = lodepng_get_bpp(mode_in);
- unsigned bits_done = (stats->bits == 1 && bpp == 1) ? 1 : 0;
- unsigned sixteen = 0; /* whether the input image is 16 bit */
- unsigned maxnumcolors = 257;
- if(bpp <= 8) maxnumcolors = LODEPNG_MIN(257, stats->numcolors + (1u << bpp));
-
- stats->numpixels += numpixels;
-
- /*if palette not allowed, no need to compute numcolors*/
- if(!stats->allow_palette) numcolors_done = 1;
-
- color_tree_init(&tree);
-
- /*If the stats was already filled in from previous data, fill its palette in tree
- and mark things as done already if we know they are the most expensive case already*/
- if(stats->alpha) alpha_done = 1;
- if(stats->colored) colored_done = 1;
- if(stats->bits == 16) numcolors_done = 1;
- if(stats->bits >= bpp) bits_done = 1;
- if(stats->numcolors >= maxnumcolors) numcolors_done = 1;
-
- if(!numcolors_done) {
- for(i = 0; i < stats->numcolors; i++) {
- const unsigned char* color = &stats->palette[i * 4];
- error = color_tree_add(&tree, color[0], color[1], color[2], color[3], i);
- if(error) goto cleanup;
- }
- }
-
- /*Check if the 16-bit input is truly 16-bit*/
- if(mode_in->bitdepth == 16 && !sixteen) {
- unsigned short r = 0, g = 0, b = 0, a = 0;
- for(i = 0; i != numpixels; ++i) {
- getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
- if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) ||
- (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ {
- stats->bits = 16;
- sixteen = 1;
- bits_done = 1;
- numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/
- break;
- }
- }
- }
-
- if(sixteen) {
- unsigned short r = 0, g = 0, b = 0, a = 0;
-
- for(i = 0; i != numpixels; ++i) {
- getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
-
- if(!colored_done && (r != g || r != b)) {
- stats->colored = 1;
- colored_done = 1;
- }
-
- if(!alpha_done) {
- unsigned matchkey = (r == stats->key_r && g == stats->key_g && b == stats->key_b);
- if(a != 65535 && (a != 0 || (stats->key && !matchkey))) {
- stats->alpha = 1;
- stats->key = 0;
- alpha_done = 1;
- } else if(a == 0 && !stats->alpha && !stats->key) {
- stats->key = 1;
- stats->key_r = r;
- stats->key_g = g;
- stats->key_b = b;
- } else if(a == 65535 && stats->key && matchkey) {
- /* Color key cannot be used if an opaque pixel also has that RGB color. */
- stats->alpha = 1;
- stats->key = 0;
- alpha_done = 1;
- }
- }
- if(alpha_done && numcolors_done && colored_done && bits_done) break;
- }
-
- if(stats->key && !stats->alpha) {
- for(i = 0; i != numpixels; ++i) {
- getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
- if(a != 0 && r == stats->key_r && g == stats->key_g && b == stats->key_b) {
- /* Color key cannot be used if an opaque pixel also has that RGB color. */
- stats->alpha = 1;
- stats->key = 0;
- alpha_done = 1;
- }
- }
- }
- } else /* < 16-bit */ {
- unsigned char r = 0, g = 0, b = 0, a = 0;
- for(i = 0; i != numpixels; ++i) {
- getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in);
-
- if(!bits_done && stats->bits < 8) {
- /*only r is checked, < 8 bits is only relevant for grayscale*/
- unsigned bits = getValueRequiredBits(r);
- if(bits > stats->bits) stats->bits = bits;
- }
- bits_done = (stats->bits >= bpp);
-
- if(!colored_done && (r != g || r != b)) {
- stats->colored = 1;
- colored_done = 1;
- if(stats->bits < 8) stats->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/
- }
-
- if(!alpha_done) {
- unsigned matchkey = (r == stats->key_r && g == stats->key_g && b == stats->key_b);
- if(a != 255 && (a != 0 || (stats->key && !matchkey))) {
- stats->alpha = 1;
- stats->key = 0;
- alpha_done = 1;
- if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
- } else if(a == 0 && !stats->alpha && !stats->key) {
- stats->key = 1;
- stats->key_r = r;
- stats->key_g = g;
- stats->key_b = b;
- } else if(a == 255 && stats->key && matchkey) {
- /* Color key cannot be used if an opaque pixel also has that RGB color. */
- stats->alpha = 1;
- stats->key = 0;
- alpha_done = 1;
- if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
- }
- }
-
- if(!numcolors_done) {
- if(!color_tree_has(&tree, r, g, b, a)) {
- error = color_tree_add(&tree, r, g, b, a, stats->numcolors);
- if(error) goto cleanup;
- if(stats->numcolors < 256) {
- unsigned char* p = stats->palette;
- unsigned n = stats->numcolors;
- p[n * 4 + 0] = r;
- p[n * 4 + 1] = g;
- p[n * 4 + 2] = b;
- p[n * 4 + 3] = a;
- }
- ++stats->numcolors;
- numcolors_done = stats->numcolors >= maxnumcolors;
- }
- }
-
- if(alpha_done && numcolors_done && colored_done && bits_done) break;
- }
-
- if(stats->key && !stats->alpha) {
- for(i = 0; i != numpixels; ++i) {
- getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in);
- if(a != 0 && r == stats->key_r && g == stats->key_g && b == stats->key_b) {
- /* Color key cannot be used if an opaque pixel also has that RGB color. */
- stats->alpha = 1;
- stats->key = 0;
- alpha_done = 1;
- if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
- }
- }
- }
-
- /*make the stats's key always 16-bit for consistency - repeat each byte twice*/
- stats->key_r += (stats->key_r << 8);
- stats->key_g += (stats->key_g << 8);
- stats->key_b += (stats->key_b << 8);
- }
-
-cleanup:
- color_tree_cleanup(&tree);
- return error;
-}
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-/*Adds a single color to the color stats. The stats must already have been inited. The color must be given as 16-bit
-(with 2 bytes repeating for 8-bit and 65535 for opaque alpha channel). This function is expensive, do not call it for
-all pixels of an image but only for a few additional values. */
-static unsigned lodepng_color_stats_add(LodePNGColorStats* stats,
- unsigned r, unsigned g, unsigned b, unsigned a) {
- unsigned error = 0;
- unsigned char image[8];
- LodePNGColorMode mode;
- lodepng_color_mode_init(&mode);
- image[0] = r >> 8; image[1] = r; image[2] = g >> 8; image[3] = g;
- image[4] = b >> 8; image[5] = b; image[6] = a >> 8; image[7] = a;
- mode.bitdepth = 16;
- mode.colortype = LCT_RGBA;
- error = lodepng_compute_color_stats(stats, image, 1, 1, &mode);
- lodepng_color_mode_cleanup(&mode);
- return error;
-}
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-/*Computes a minimal PNG color model that can contain all colors as indicated by the stats.
-The stats should be computed with lodepng_compute_color_stats.
-mode_in is raw color profile of the image the stats were computed on, to copy palette order from when relevant.
-Minimal PNG color model means the color type and bit depth that gives smallest amount of bits in the output image,
-e.g. gray if only grayscale pixels, palette if less than 256 colors, color key if only single transparent color, ...
-This is used if auto_convert is enabled (it is by default).
-*/
-static unsigned auto_choose_color(LodePNGColorMode* mode_out,
- const LodePNGColorMode* mode_in,
- const LodePNGColorStats* stats) {
- unsigned error = 0;
- unsigned palettebits;
- size_t i, n;
- size_t numpixels = stats->numpixels;
- unsigned palette_ok, gray_ok;
-
- unsigned alpha = stats->alpha;
- unsigned key = stats->key;
- unsigned bits = stats->bits;
-
- mode_out->key_defined = 0;
-
- if(key && numpixels <= 16) {
- alpha = 1; /*too few pixels to justify tRNS chunk overhead*/
- key = 0;
- if(bits < 8) bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
- }
-
- gray_ok = !stats->colored;
- if(!stats->allow_greyscale) gray_ok = 0;
- if(!gray_ok && bits < 8) bits = 8;
-
- n = stats->numcolors;
- palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8));
- palette_ok = n <= 256 && bits <= 8 && n != 0; /*n==0 means likely numcolors wasn't computed*/
- if(numpixels < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/
- if(gray_ok && !alpha && bits <= palettebits) palette_ok = 0; /*gray is less overhead*/
- if(!stats->allow_palette) palette_ok = 0;
-
- if(palette_ok) {
- const unsigned char* p = stats->palette;
- lodepng_palette_clear(mode_out); /*remove potential earlier palette*/
- for(i = 0; i != stats->numcolors; ++i) {
- error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]);
- if(error) break;
- }
-
- mode_out->colortype = LCT_PALETTE;
- mode_out->bitdepth = palettebits;
-
- if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize
- && mode_in->bitdepth == mode_out->bitdepth) {
- /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/
- lodepng_color_mode_cleanup(mode_out); /*clears palette, keeps the above set colortype and bitdepth fields as-is*/
- lodepng_color_mode_copy(mode_out, mode_in);
- }
- } else /*8-bit or 16-bit per channel*/ {
- mode_out->bitdepth = bits;
- mode_out->colortype = alpha ? (gray_ok ? LCT_GREY_ALPHA : LCT_RGBA)
- : (gray_ok ? LCT_GREY : LCT_RGB);
- if(key) {
- unsigned mask = (1u << mode_out->bitdepth) - 1u; /*stats always uses 16-bit, mask converts it*/
- mode_out->key_r = stats->key_r & mask;
- mode_out->key_g = stats->key_g & mask;
- mode_out->key_b = stats->key_b & mask;
- mode_out->key_defined = 1;
- }
- }
-
- return error;
-}
-
-#endif /* #ifdef LODEPNG_COMPILE_ENCODER */
-
-/*Paeth predictor, used by PNG filter type 4*/
-static unsigned char paethPredictor(unsigned char a, unsigned char b, unsigned char c) {
- /* the subtractions of unsigned char cast it to a signed type.
- With gcc, short is faster than int, with clang int is as fast (as of april 2023)*/
- short pa = (b - c) < 0 ? -(b - c) : (b - c);
- short pb = (a - c) < 0 ? -(a - c) : (a - c);
- /* writing it out like this compiles to something faster than introducing a temp variable*/
- short pc = (a + b - c - c) < 0 ? -(a + b - c - c) : (a + b - c - c);
- /* return input value associated with smallest of pa, pb, pc (with certain priority if equal) */
- if(pb < pa) { a = b; pa = pb; }
- return (pc < pa) ? c : a;
-}
-
-/*shared values used by multiple Adam7 related functions*/
-
-static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
-static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
-static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
-static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
-
-/*
-Outputs various dimensions and positions in the image related to the Adam7 reduced images.
-passw: output containing the width of the 7 passes
-passh: output containing the height of the 7 passes
-filter_passstart: output containing the index of the start and end of each
- reduced image with filter bytes
-padded_passstart output containing the index of the start and end of each
- reduced image when without filter bytes but with padded scanlines
-passstart: output containing the index of the start and end of each reduced
- image without padding between scanlines, but still padding between the images
-w, h: width and height of non-interlaced image
-bpp: bits per pixel
-"padded" is only relevant if bpp is less than 8 and a scanline or image does not
- end at a full byte
-*/
-static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8],
- size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) {
- /*the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass*/
- unsigned i;
-
- /*calculate width and height in pixels of each pass*/
- for(i = 0; i != 7; ++i) {
- passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i];
- passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i];
- if(passw[i] == 0) passh[i] = 0;
- if(passh[i] == 0) passw[i] = 0;
- }
-
- filter_passstart[0] = padded_passstart[0] = passstart[0] = 0;
- for(i = 0; i != 7; ++i) {
- /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/
- filter_passstart[i + 1] = filter_passstart[i]
- + ((passw[i] && passh[i]) ? passh[i] * (1u + (passw[i] * bpp + 7u) / 8u) : 0);
- /*bits padded if needed to fill full byte at end of each scanline*/
- padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7u) / 8u);
- /*only padded at end of reduced image*/
- passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7u) / 8u;
- }
-}
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / PNG Decoder / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-/*read the information from the header and store it in the LodePNGInfo. return value is error*/
-unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state,
- const unsigned char* in, size_t insize) {
- unsigned width, height;
- LodePNGInfo* info = &state->info_png;
- if(insize == 0 || in == 0) {
- CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/
- }
- if(insize < 33) {
- CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/
- }
-
- /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/
- /* TODO: remove this. One should use a new LodePNGState for new sessions */
- lodepng_info_cleanup(info);
- lodepng_info_init(info);
-
- if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71
- || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) {
- CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/
- }
- if(lodepng_chunk_length(in + 8) != 13) {
- CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/
- }
- if(!lodepng_chunk_type_equals(in + 8, "IHDR")) {
- CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/
- }
-
- /*read the values given in the header*/
- width = lodepng_read32bitInt(&in[16]);
- height = lodepng_read32bitInt(&in[20]);
- /*TODO: remove the undocumented feature that allows to give null pointers to width or height*/
- if(w) *w = width;
- if(h) *h = height;
- info->color.bitdepth = in[24];
- info->color.colortype = (LodePNGColorType)in[25];
- info->compression_method = in[26];
- info->filter_method = in[27];
- info->interlace_method = in[28];
-
- /*errors returned only after the parsing so other values are still output*/
-
- /*error: invalid image size*/
- if(width == 0 || height == 0) CERROR_RETURN_ERROR(state->error, 93);
- /*error: invalid colortype or bitdepth combination*/
- state->error = checkColorValidity(info->color.colortype, info->color.bitdepth);
- if(state->error) return state->error;
- /*error: only compression method 0 is allowed in the specification*/
- if(info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32);
- /*error: only filter method 0 is allowed in the specification*/
- if(info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33);
- /*error: only interlace methods 0 and 1 exist in the specification*/
- if(info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34);
-
- if(!state->decoder.ignore_crc) {
- unsigned CRC = lodepng_read32bitInt(&in[29]);
- unsigned checksum = lodepng_crc32(&in[12], 17);
- if(CRC != checksum) {
- CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/
- }
- }
-
- return state->error;
-}
-
-static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon,
- size_t bytewidth, unsigned char filterType, size_t length) {
- /*
- For PNG filter method 0
- unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte,
- the filter works byte per byte (bytewidth = 1)
- precon is the previous unfiltered scanline, recon the result, scanline the current one
- the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead
- recon and scanline MAY be the same memory address! precon must be disjoint.
- */
-
- size_t i;
- switch(filterType) {
- case 0:
- for(i = 0; i != length; ++i) recon[i] = scanline[i];
- break;
- case 1: {
- size_t j = 0;
- for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
- for(i = bytewidth; i != length; ++i, ++j) recon[i] = scanline[i] + recon[j];
- break;
- }
- case 2:
- if(precon) {
- for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i];
- } else {
- for(i = 0; i != length; ++i) recon[i] = scanline[i];
- }
- break;
- case 3:
- if(precon) {
- size_t j = 0;
- for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u);
- /* Unroll independent paths of this predictor. A 6x and 8x version is also possible but that adds
- too much code. Whether this speeds up anything depends on compiler and settings. */
- if(bytewidth >= 4) {
- for(; i + 3 < length; i += 4, j += 4) {
- unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3];
- unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3];
- unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3];
- recon[i + 0] = s0 + ((r0 + p0) >> 1u);
- recon[i + 1] = s1 + ((r1 + p1) >> 1u);
- recon[i + 2] = s2 + ((r2 + p2) >> 1u);
- recon[i + 3] = s3 + ((r3 + p3) >> 1u);
- }
- } else if(bytewidth >= 3) {
- for(; i + 2 < length; i += 3, j += 3) {
- unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2];
- unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2];
- unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2];
- recon[i + 0] = s0 + ((r0 + p0) >> 1u);
- recon[i + 1] = s1 + ((r1 + p1) >> 1u);
- recon[i + 2] = s2 + ((r2 + p2) >> 1u);
- }
- } else if(bytewidth >= 2) {
- for(; i + 1 < length; i += 2, j += 2) {
- unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1];
- unsigned char r0 = recon[j + 0], r1 = recon[j + 1];
- unsigned char p0 = precon[i + 0], p1 = precon[i + 1];
- recon[i + 0] = s0 + ((r0 + p0) >> 1u);
- recon[i + 1] = s1 + ((r1 + p1) >> 1u);
- }
- }
- for(; i != length; ++i, ++j) recon[i] = scanline[i] + ((recon[j] + precon[i]) >> 1u);
- } else {
- size_t j = 0;
- for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
- for(i = bytewidth; i != length; ++i, ++j) recon[i] = scanline[i] + (recon[j] >> 1u);
- }
- break;
- case 4:
- if(precon) {
- /* Unroll independent paths of this predictor. Whether this speeds up
- anything depends on compiler and settings. */
- if(bytewidth == 8) {
- unsigned char a0, b0 = 0, c0, d0 = 0, a1, b1 = 0, c1, d1 = 0;
- unsigned char a2, b2 = 0, c2, d2 = 0, a3, b3 = 0, c3, d3 = 0;
- unsigned char a4, b4 = 0, c4, d4 = 0, a5, b5 = 0, c5, d5 = 0;
- unsigned char a6, b6 = 0, c6, d6 = 0, a7, b7 = 0, c7, d7 = 0;
- for(i = 0; i + 7 < length; i += 8) {
- c0 = b0; c1 = b1; c2 = b2; c3 = b3;
- c4 = b4; c5 = b5; c6 = b6; c7 = b7;
- b0 = precon[i + 0]; b1 = precon[i + 1]; b2 = precon[i + 2]; b3 = precon[i + 3];
- b4 = precon[i + 4]; b5 = precon[i + 5]; b6 = precon[i + 6]; b7 = precon[i + 7];
- a0 = d0; a1 = d1; a2 = d2; a3 = d3;
- a4 = d4; a5 = d5; a6 = d6; a7 = d7;
- d0 = scanline[i + 0] + paethPredictor(a0, b0, c0);
- d1 = scanline[i + 1] + paethPredictor(a1, b1, c1);
- d2 = scanline[i + 2] + paethPredictor(a2, b2, c2);
- d3 = scanline[i + 3] + paethPredictor(a3, b3, c3);
- d4 = scanline[i + 4] + paethPredictor(a4, b4, c4);
- d5 = scanline[i + 5] + paethPredictor(a5, b5, c5);
- d6 = scanline[i + 6] + paethPredictor(a6, b6, c6);
- d7 = scanline[i + 7] + paethPredictor(a7, b7, c7);
- recon[i + 0] = d0; recon[i + 1] = d1; recon[i + 2] = d2; recon[i + 3] = d3;
- recon[i + 4] = d4; recon[i + 5] = d5; recon[i + 6] = d6; recon[i + 7] = d7;
- }
- } else if(bytewidth == 6) {
- unsigned char a0, b0 = 0, c0, d0 = 0, a1, b1 = 0, c1, d1 = 0;
- unsigned char a2, b2 = 0, c2, d2 = 0, a3, b3 = 0, c3, d3 = 0;
- unsigned char a4, b4 = 0, c4, d4 = 0, a5, b5 = 0, c5, d5 = 0;
- for(i = 0; i + 5 < length; i += 6) {
- c0 = b0; c1 = b1; c2 = b2;
- c3 = b3; c4 = b4; c5 = b5;
- b0 = precon[i + 0]; b1 = precon[i + 1]; b2 = precon[i + 2];
- b3 = precon[i + 3]; b4 = precon[i + 4]; b5 = precon[i + 5];
- a0 = d0; a1 = d1; a2 = d2;
- a3 = d3; a4 = d4; a5 = d5;
- d0 = scanline[i + 0] + paethPredictor(a0, b0, c0);
- d1 = scanline[i + 1] + paethPredictor(a1, b1, c1);
- d2 = scanline[i + 2] + paethPredictor(a2, b2, c2);
- d3 = scanline[i + 3] + paethPredictor(a3, b3, c3);
- d4 = scanline[i + 4] + paethPredictor(a4, b4, c4);
- d5 = scanline[i + 5] + paethPredictor(a5, b5, c5);
- recon[i + 0] = d0; recon[i + 1] = d1; recon[i + 2] = d2;
- recon[i + 3] = d3; recon[i + 4] = d4; recon[i + 5] = d5;
- }
- } else if(bytewidth == 4) {
- unsigned char a0, b0 = 0, c0, d0 = 0, a1, b1 = 0, c1, d1 = 0;
- unsigned char a2, b2 = 0, c2, d2 = 0, a3, b3 = 0, c3, d3 = 0;
- for(i = 0; i + 3 < length; i += 4) {
- c0 = b0; c1 = b1; c2 = b2; c3 = b3;
- b0 = precon[i + 0]; b1 = precon[i + 1]; b2 = precon[i + 2]; b3 = precon[i + 3];
- a0 = d0; a1 = d1; a2 = d2; a3 = d3;
- d0 = scanline[i + 0] + paethPredictor(a0, b0, c0);
- d1 = scanline[i + 1] + paethPredictor(a1, b1, c1);
- d2 = scanline[i + 2] + paethPredictor(a2, b2, c2);
- d3 = scanline[i + 3] + paethPredictor(a3, b3, c3);
- recon[i + 0] = d0; recon[i + 1] = d1; recon[i + 2] = d2; recon[i + 3] = d3;
- }
- } else if(bytewidth == 3) {
- unsigned char a0, b0 = 0, c0, d0 = 0;
- unsigned char a1, b1 = 0, c1, d1 = 0;
- unsigned char a2, b2 = 0, c2, d2 = 0;
- for(i = 0; i + 2 < length; i += 3) {
- c0 = b0; c1 = b1; c2 = b2;
- b0 = precon[i + 0]; b1 = precon[i + 1]; b2 = precon[i + 2];
- a0 = d0; a1 = d1; a2 = d2;
- d0 = scanline[i + 0] + paethPredictor(a0, b0, c0);
- d1 = scanline[i + 1] + paethPredictor(a1, b1, c1);
- d2 = scanline[i + 2] + paethPredictor(a2, b2, c2);
- recon[i + 0] = d0; recon[i + 1] = d1; recon[i + 2] = d2;
- }
- } else if(bytewidth == 2) {
- unsigned char a0, b0 = 0, c0, d0 = 0;
- unsigned char a1, b1 = 0, c1, d1 = 0;
- for(i = 0; i + 1 < length; i += 2) {
- c0 = b0; c1 = b1;
- b0 = precon[i + 0];
- b1 = precon[i + 1];
- a0 = d0; a1 = d1;
- d0 = scanline[i + 0] + paethPredictor(a0, b0, c0);
- d1 = scanline[i + 1] + paethPredictor(a1, b1, c1);
- recon[i + 0] = d0;
- recon[i + 1] = d1;
- }
- } else if(bytewidth == 1) {
- unsigned char a, b = 0, c, d = 0;
- for(i = 0; i != length; ++i) {
- c = b;
- b = precon[i];
- a = d;
- d = scanline[i] + paethPredictor(a, b, c);
- recon[i] = d;
- }
- } else {
- /* Normally not a possible case, but this would handle it correctly */
- for(i = 0; i != bytewidth; ++i) {
- recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/
- }
- }
- /* finish any remaining bytes */
- for(; i != length; ++i) {
- recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]));
- }
- } else {
- size_t j = 0;
- for(i = 0; i != bytewidth; ++i) {
- recon[i] = scanline[i];
- }
- for(i = bytewidth; i != length; ++i, ++j) {
- /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/
- recon[i] = (scanline[i] + recon[j]);
- }
- }
- break;
- default: return 36; /*error: invalid filter type given*/
- }
- return 0;
-}
-
-static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) {
- /*
- For PNG filter method 0
- this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times)
- out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline
- w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel
- in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes)
- */
-
- unsigned y;
- unsigned char* prevline = 0;
-
- /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
- size_t bytewidth = (bpp + 7u) / 8u;
- /*the width of a scanline in bytes, not including the filter type*/
- size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u;
-
- for(y = 0; y < h; ++y) {
- size_t outindex = linebytes * y;
- size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
- unsigned char filterType = in[inindex];
-
- CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes));
-
- prevline = &out[outindex];
- }
-
- return 0;
-}
-
-/*
-in: Adam7 interlaced image, with no padding bits between scanlines, but between
- reduced images so that each reduced image starts at a byte.
-out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h
-bpp: bits per pixel
-out has the following size in bits: w * h * bpp.
-in is possibly bigger due to padding bits between reduced images.
-out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation
-(because that's likely a little bit faster)
-NOTE: comments about padding bits are only relevant if bpp < 8
-*/
-static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) {
- unsigned passw[7], passh[7];
- size_t filter_passstart[8], padded_passstart[8], passstart[8];
- unsigned i;
-
- Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
-
- if(bpp >= 8) {
- for(i = 0; i != 7; ++i) {
- unsigned x, y, b;
- size_t bytewidth = bpp / 8u;
- for(y = 0; y < passh[i]; ++y)
- for(x = 0; x < passw[i]; ++x) {
- size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth;
- size_t pixeloutstart = ((ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * (size_t)w
- + ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bytewidth;
- for(b = 0; b < bytewidth; ++b) {
- out[pixeloutstart + b] = in[pixelinstart + b];
- }
- }
- }
- } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ {
- for(i = 0; i != 7; ++i) {
- unsigned x, y, b;
- unsigned ilinebits = bpp * passw[i];
- unsigned olinebits = bpp * w;
- size_t obp, ibp; /*bit pointers (for out and in buffer)*/
- for(y = 0; y < passh[i]; ++y)
- for(x = 0; x < passw[i]; ++x) {
- ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
- obp = (ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bpp;
- for(b = 0; b < bpp; ++b) {
- unsigned char bit = readBitFromReversedStream(&ibp, in);
- setBitOfReversedStream(&obp, out, bit);
- }
- }
- }
- }
-}
-
-static void removePaddingBits(unsigned char* out, const unsigned char* in,
- size_t olinebits, size_t ilinebits, unsigned h) {
- /*
- After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need
- to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers
- for the Adam7 code, the color convert code and the output to the user.
- in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must
- have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits
- also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7
- only useful if (ilinebits - olinebits) is a value in the range 1..7
- */
- unsigned y;
- size_t diff = ilinebits - olinebits;
- size_t ibp = 0, obp = 0; /*input and output bit pointers*/
- for(y = 0; y < h; ++y) {
- size_t x;
- for(x = 0; x < olinebits; ++x) {
- unsigned char bit = readBitFromReversedStream(&ibp, in);
- setBitOfReversedStream(&obp, out, bit);
- }
- ibp += diff;
- }
-}
-
-/*out must be buffer big enough to contain full image, and in must contain the full decompressed data from
-the IDAT chunks (with filter index bytes and possible padding bits)
-return value is error*/
-static unsigned postProcessScanlines(unsigned char* out, unsigned char* in,
- unsigned w, unsigned h, const LodePNGInfo* info_png) {
- /*
- This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype.
- Steps:
- *) if no Adam7: 1) unfilter 2) remove padding bits (= possible extra bits per scanline if bpp < 8)
- *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace
- NOTE: the in buffer will be overwritten with intermediate data!
- */
- unsigned bpp = lodepng_get_bpp(&info_png->color);
- if(bpp == 0) return 31; /*error: invalid colortype*/
-
- if(info_png->interlace_method == 0) {
- if(bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) {
- CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp));
- removePaddingBits(out, in, w * bpp, ((w * bpp + 7u) / 8u) * 8u, h);
- }
- /*we can immediately filter into the out buffer, no other steps needed*/
- else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp));
- } else /*interlace_method is 1 (Adam7)*/ {
- unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
- unsigned i;
-
- Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
-
- for(i = 0; i != 7; ++i) {
- CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp));
- /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline,
- move bytes instead of bits or move not at all*/
- if(bpp < 8) {
- /*remove padding bits in scanlines; after this there still may be padding
- bits between the different reduced images: each reduced image still starts nicely at a byte*/
- removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp,
- ((passw[i] * bpp + 7u) / 8u) * 8u, passh[i]);
- }
- }
-
- Adam7_deinterlace(out, in, w, h, bpp);
- }
-
- return 0;
-}
-
-static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) {
- unsigned pos = 0, i;
- color->palettesize = chunkLength / 3u;
- if(color->palettesize == 0 || color->palettesize > 256) return 38; /*error: palette too small or big*/
- lodepng_color_mode_alloc_palette(color);
- if(!color->palette && color->palettesize) {
- color->palettesize = 0;
- return 83; /*alloc fail*/
- }
-
- for(i = 0; i != color->palettesize; ++i) {
- color->palette[4 * i + 0] = data[pos++]; /*R*/
- color->palette[4 * i + 1] = data[pos++]; /*G*/
- color->palette[4 * i + 2] = data[pos++]; /*B*/
- color->palette[4 * i + 3] = 255; /*alpha*/
- }
-
- return 0; /* OK */
-}
-
-static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) {
- unsigned i;
- if(color->colortype == LCT_PALETTE) {
- /*error: more alpha values given than there are palette entries*/
- if(chunkLength > color->palettesize) return 39;
-
- for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i];
- } else if(color->colortype == LCT_GREY) {
- /*error: this chunk must be 2 bytes for grayscale image*/
- if(chunkLength != 2) return 30;
-
- color->key_defined = 1;
- color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1];
- } else if(color->colortype == LCT_RGB) {
- /*error: this chunk must be 6 bytes for RGB image*/
- if(chunkLength != 6) return 41;
-
- color->key_defined = 1;
- color->key_r = 256u * data[0] + data[1];
- color->key_g = 256u * data[2] + data[3];
- color->key_b = 256u * data[4] + data[5];
- }
- else return 42; /*error: tRNS chunk not allowed for other color models*/
-
- return 0; /* OK */
-}
-
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-/*background color chunk (bKGD)*/
-static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
- if(info->color.colortype == LCT_PALETTE) {
- /*error: this chunk must be 1 byte for indexed color image*/
- if(chunkLength != 1) return 43;
-
- /*error: invalid palette index, or maybe this chunk appeared before PLTE*/
- if(data[0] >= info->color.palettesize) return 103;
-
- info->background_defined = 1;
- info->background_r = info->background_g = info->background_b = data[0];
- } else if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) {
- /*error: this chunk must be 2 bytes for grayscale image*/
- if(chunkLength != 2) return 44;
-
- /*the values are truncated to bitdepth in the PNG file*/
- info->background_defined = 1;
- info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1];
- } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) {
- /*error: this chunk must be 6 bytes for grayscale image*/
- if(chunkLength != 6) return 45;
-
- /*the values are truncated to bitdepth in the PNG file*/
- info->background_defined = 1;
- info->background_r = 256u * data[0] + data[1];
- info->background_g = 256u * data[2] + data[3];
- info->background_b = 256u * data[4] + data[5];
- }
-
- return 0; /* OK */
-}
-
-/*text chunk (tEXt)*/
-static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
- unsigned error = 0;
- char *key = 0, *str = 0;
-
- while(!error) /*not really a while loop, only used to break on error*/ {
- unsigned length, string2_begin;
-
- length = 0;
- while(length < chunkLength && data[length] != 0) ++length;
- /*even though it's not allowed by the standard, no error is thrown if
- there's no null termination char, if the text is empty*/
- if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/
-
- key = (char*)lodepng_malloc(length + 1);
- if(!key) CERROR_BREAK(error, 83); /*alloc fail*/
-
- lodepng_memcpy(key, data, length);
- key[length] = 0;
-
- string2_begin = length + 1; /*skip keyword null terminator*/
-
- length = (unsigned)(chunkLength < string2_begin ? 0 : chunkLength - string2_begin);
- str = (char*)lodepng_malloc(length + 1);
- if(!str) CERROR_BREAK(error, 83); /*alloc fail*/
-
- lodepng_memcpy(str, data + string2_begin, length);
- str[length] = 0;
-
- error = lodepng_add_text(info, key, str);
-
- break;
- }
-
- lodepng_free(key);
- lodepng_free(str);
-
- return error;
-}
-
-/*compressed text chunk (zTXt)*/
-static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
- const unsigned char* data, size_t chunkLength) {
- unsigned error = 0;
-
- /*copy the object to change parameters in it*/
- LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
-
- unsigned length, string2_begin;
- char *key = 0;
- unsigned char* str = 0;
- size_t size = 0;
-
- while(!error) /*not really a while loop, only used to break on error*/ {
- for(length = 0; length < chunkLength && data[length] != 0; ++length) ;
- if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/
- if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/
-
- key = (char*)lodepng_malloc(length + 1);
- if(!key) CERROR_BREAK(error, 83); /*alloc fail*/
-
- lodepng_memcpy(key, data, length);
- key[length] = 0;
-
- if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/
-
- string2_begin = length + 2;
- if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/
-
- length = (unsigned)chunkLength - string2_begin;
- zlibsettings.max_output_size = decoder->max_text_size;
- /*will fail if zlib error, e.g. if length is too small*/
- error = zlib_decompress(&str, &size, 0, &data[string2_begin],
- length, &zlibsettings);
- /*error: compressed text larger than decoder->max_text_size*/
- if(error && size > zlibsettings.max_output_size) error = 112;
- if(error) break;
- error = lodepng_add_text_sized(info, key, (char*)str, size);
- break;
- }
-
- lodepng_free(key);
- lodepng_free(str);
-
- return error;
-}
-
-/*international text chunk (iTXt)*/
-static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
- const unsigned char* data, size_t chunkLength) {
- unsigned error = 0;
- unsigned i;
-
- /*copy the object to change parameters in it*/
- LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
-
- unsigned length, begin, compressed;
- char *key = 0, *langtag = 0, *transkey = 0;
-
- while(!error) /*not really a while loop, only used to break on error*/ {
- /*Quick check if the chunk length isn't too small. Even without check
- it'd still fail with other error checks below if it's too short. This just gives a different error code.*/
- if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/
-
- /*read the key*/
- for(length = 0; length < chunkLength && data[length] != 0; ++length) ;
- if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/
- if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/
-
- key = (char*)lodepng_malloc(length + 1);
- if(!key) CERROR_BREAK(error, 83); /*alloc fail*/
-
- lodepng_memcpy(key, data, length);
- key[length] = 0;
-
- /*read the compression method*/
- compressed = data[length + 1];
- if(data[length + 2] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/
-
- /*even though it's not allowed by the standard, no error is thrown if
- there's no null termination char, if the text is empty for the next 3 texts*/
-
- /*read the langtag*/
- begin = length + 3;
- length = 0;
- for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length;
-
- langtag = (char*)lodepng_malloc(length + 1);
- if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/
-
- lodepng_memcpy(langtag, data + begin, length);
- langtag[length] = 0;
-
- /*read the transkey*/
- begin += length + 1;
- length = 0;
- for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length;
-
- transkey = (char*)lodepng_malloc(length + 1);
- if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/
-
- lodepng_memcpy(transkey, data + begin, length);
- transkey[length] = 0;
-
- /*read the actual text*/
- begin += length + 1;
-
- length = (unsigned)chunkLength < begin ? 0 : (unsigned)chunkLength - begin;
-
- if(compressed) {
- unsigned char* str = 0;
- size_t size = 0;
- zlibsettings.max_output_size = decoder->max_text_size;
- /*will fail if zlib error, e.g. if length is too small*/
- error = zlib_decompress(&str, &size, 0, &data[begin],
- length, &zlibsettings);
- /*error: compressed text larger than decoder->max_text_size*/
- if(error && size > zlibsettings.max_output_size) error = 112;
- if(!error) error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)str, size);
- lodepng_free(str);
- } else {
- error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)(data + begin), length);
- }
-
- break;
- }
-
- lodepng_free(key);
- lodepng_free(langtag);
- lodepng_free(transkey);
-
- return error;
-}
-
-static unsigned readChunk_tIME(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
- if(chunkLength != 7) return 73; /*invalid tIME chunk size*/
-
- info->time_defined = 1;
- info->time.year = 256u * data[0] + data[1];
- info->time.month = data[2];
- info->time.day = data[3];
- info->time.hour = data[4];
- info->time.minute = data[5];
- info->time.second = data[6];
-
- return 0; /* OK */
-}
-
-static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
- if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/
-
- info->phys_defined = 1;
- info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3];
- info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7];
- info->phys_unit = data[8];
-
- return 0; /* OK */
-}
-
-static unsigned readChunk_gAMA(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
- if(chunkLength != 4) return 96; /*invalid gAMA chunk size*/
-
- info->gama_defined = 1;
- info->gama_gamma = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3];
-
- return 0; /* OK */
-}
-
-static unsigned readChunk_cHRM(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
- if(chunkLength != 32) return 97; /*invalid cHRM chunk size*/
-
- info->chrm_defined = 1;
- info->chrm_white_x = 16777216u * data[ 0] + 65536u * data[ 1] + 256u * data[ 2] + data[ 3];
- info->chrm_white_y = 16777216u * data[ 4] + 65536u * data[ 5] + 256u * data[ 6] + data[ 7];
- info->chrm_red_x = 16777216u * data[ 8] + 65536u * data[ 9] + 256u * data[10] + data[11];
- info->chrm_red_y = 16777216u * data[12] + 65536u * data[13] + 256u * data[14] + data[15];
- info->chrm_green_x = 16777216u * data[16] + 65536u * data[17] + 256u * data[18] + data[19];
- info->chrm_green_y = 16777216u * data[20] + 65536u * data[21] + 256u * data[22] + data[23];
- info->chrm_blue_x = 16777216u * data[24] + 65536u * data[25] + 256u * data[26] + data[27];
- info->chrm_blue_y = 16777216u * data[28] + 65536u * data[29] + 256u * data[30] + data[31];
-
- return 0; /* OK */
-}
-
-static unsigned readChunk_sRGB(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
- if(chunkLength != 1) return 98; /*invalid sRGB chunk size (this one is never ignored)*/
-
- info->srgb_defined = 1;
- info->srgb_intent = data[0];
-
- return 0; /* OK */
-}
-
-static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecoderSettings* decoder,
- const unsigned char* data, size_t chunkLength) {
- unsigned error = 0;
- unsigned i;
- size_t size = 0;
- /*copy the object to change parameters in it*/
- LodePNGDecompressSettings zlibsettings = decoder->zlibsettings;
-
- unsigned length, string2_begin;
-
- info->iccp_defined = 1;
- if(info->iccp_name) lodepng_clear_icc(info);
-
- for(length = 0; length < chunkLength && data[length] != 0; ++length) ;
- if(length + 2 >= chunkLength) return 75; /*no null termination, corrupt?*/
- if(length < 1 || length > 79) return 89; /*keyword too short or long*/
-
- info->iccp_name = (char*)lodepng_malloc(length + 1);
- if(!info->iccp_name) return 83; /*alloc fail*/
-
- info->iccp_name[length] = 0;
- for(i = 0; i != length; ++i) info->iccp_name[i] = (char)data[i];
-
- if(data[length + 1] != 0) return 72; /*the 0 byte indicating compression must be 0*/
-
- string2_begin = length + 2;
- if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/
-
- length = (unsigned)chunkLength - string2_begin;
- zlibsettings.max_output_size = decoder->max_icc_size;
- error = zlib_decompress(&info->iccp_profile, &size, 0,
- &data[string2_begin],
- length, &zlibsettings);
- /*error: ICC profile larger than decoder->max_icc_size*/
- if(error && size > zlibsettings.max_output_size) error = 113;
- info->iccp_profile_size = size;
- if(!error && !info->iccp_profile_size) error = 100; /*invalid ICC profile size*/
- return error;
-}
-
-/*significant bits chunk (sBIT)*/
-static unsigned readChunk_sBIT(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) {
- unsigned bitdepth = (info->color.colortype == LCT_PALETTE) ? 8 : info->color.bitdepth;
- if(info->color.colortype == LCT_GREY) {
- /*error: this chunk must be 1 bytes for grayscale image*/
- if(chunkLength != 1) return 114;
- if(data[0] == 0 || data[0] > bitdepth) return 115;
- info->sbit_defined = 1;
- info->sbit_r = info->sbit_g = info->sbit_b = data[0]; /*setting g and b is not required, but sensible*/
- } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_PALETTE) {
- /*error: this chunk must be 3 bytes for RGB and palette image*/
- if(chunkLength != 3) return 114;
- if(data[0] == 0 || data[1] == 0 || data[2] == 0) return 115;
- if(data[0] > bitdepth || data[1] > bitdepth || data[2] > bitdepth) return 115;
- info->sbit_defined = 1;
- info->sbit_r = data[0];
- info->sbit_g = data[1];
- info->sbit_b = data[2];
- } else if(info->color.colortype == LCT_GREY_ALPHA) {
- /*error: this chunk must be 2 byte for grayscale with alpha image*/
- if(chunkLength != 2) return 114;
- if(data[0] == 0 || data[1] == 0) return 115;
- if(data[0] > bitdepth || data[1] > bitdepth) return 115;
- info->sbit_defined = 1;
- info->sbit_r = info->sbit_g = info->sbit_b = data[0]; /*setting g and b is not required, but sensible*/
- info->sbit_a = data[1];
- } else if(info->color.colortype == LCT_RGBA) {
- /*error: this chunk must be 4 bytes for grayscale image*/
- if(chunkLength != 4) return 114;
- if(data[0] == 0 || data[1] == 0 || data[2] == 0 || data[3] == 0) return 115;
- if(data[0] > bitdepth || data[1] > bitdepth || data[2] > bitdepth || data[3] > bitdepth) return 115;
- info->sbit_defined = 1;
- info->sbit_r = data[0];
- info->sbit_g = data[1];
- info->sbit_b = data[2];
- info->sbit_a = data[3];
- }
-
- return 0; /* OK */
-}
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
- const unsigned char* in, size_t insize) {
- const unsigned char* chunk = in + pos;
- unsigned chunkLength;
- const unsigned char* data;
- unsigned unhandled = 0;
- unsigned error = 0;
-
- if(pos + 4 > insize) return 30;
- chunkLength = lodepng_chunk_length(chunk);
- if(chunkLength > 2147483647) return 63;
- data = lodepng_chunk_data_const(chunk);
- if(chunkLength + 12 > insize - pos) return 30;
-
- if(lodepng_chunk_type_equals(chunk, "PLTE")) {
- error = readChunk_PLTE(&state->info_png.color, data, chunkLength);
- } else if(lodepng_chunk_type_equals(chunk, "tRNS")) {
- error = readChunk_tRNS(&state->info_png.color, data, chunkLength);
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- } else if(lodepng_chunk_type_equals(chunk, "bKGD")) {
- error = readChunk_bKGD(&state->info_png, data, chunkLength);
- } else if(lodepng_chunk_type_equals(chunk, "tEXt")) {
- error = readChunk_tEXt(&state->info_png, data, chunkLength);
- } else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
- error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength);
- } else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
- error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength);
- } else if(lodepng_chunk_type_equals(chunk, "tIME")) {
- error = readChunk_tIME(&state->info_png, data, chunkLength);
- } else if(lodepng_chunk_type_equals(chunk, "pHYs")) {
- error = readChunk_pHYs(&state->info_png, data, chunkLength);
- } else if(lodepng_chunk_type_equals(chunk, "gAMA")) {
- error = readChunk_gAMA(&state->info_png, data, chunkLength);
- } else if(lodepng_chunk_type_equals(chunk, "cHRM")) {
- error = readChunk_cHRM(&state->info_png, data, chunkLength);
- } else if(lodepng_chunk_type_equals(chunk, "sRGB")) {
- error = readChunk_sRGB(&state->info_png, data, chunkLength);
- } else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
- error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength);
- } else if(lodepng_chunk_type_equals(chunk, "sBIT")) {
- error = readChunk_sBIT(&state->info_png, data, chunkLength);
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
- } else {
- /* unhandled chunk is ok (is not an error) */
- unhandled = 1;
- }
-
- if(!error && !unhandled && !state->decoder.ignore_crc) {
- if(lodepng_chunk_check_crc(chunk)) return 57; /*invalid CRC*/
- }
-
- return error;
-}
-
-/*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/
-static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h,
- LodePNGState* state,
- const unsigned char* in, size_t insize) {
- unsigned char IEND = 0;
- const unsigned char* chunk; /*points to beginning of next chunk*/
- unsigned char* idat; /*the data from idat chunks, zlib compressed*/
- size_t idatsize = 0;
- unsigned char* scanlines = 0;
- size_t scanlines_size = 0, expected_size = 0;
- size_t outsize = 0;
-
- /*for unknown chunk order*/
- unsigned unknown = 0;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-
- /* safe output values in case error happens */
- *out = 0;
- *w = *h = 0;
-
- state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/
- if(state->error) return;
-
- if(lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) {
- CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/
- }
-
- /*the input filesize is a safe upper bound for the sum of idat chunks size*/
- idat = (unsigned char*)lodepng_malloc(insize);
- if(!idat) CERROR_RETURN(state->error, 83); /*alloc fail*/
-
- chunk = &in[33]; /*first byte of the first chunk after the header*/
-
- /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk.
- IDAT data is put at the start of the in buffer*/
- while(!IEND && !state->error) {
- unsigned chunkLength;
- const unsigned char* data; /*the data in the chunk*/
- size_t pos = (size_t)(chunk - in);
-
- /*error: next chunk out of bounds of the in buffer*/
- if(chunk < in || pos + 12 > insize) {
- if(state->decoder.ignore_end) break; /*other errors may still happen though*/
- CERROR_BREAK(state->error, 30);
- }
-
- /*length of the data of the chunk, excluding the 12 bytes for length, chunk type and CRC*/
- chunkLength = lodepng_chunk_length(chunk);
- /*error: chunk length larger than the max PNG chunk size*/
- if(chunkLength > 2147483647) {
- if(state->decoder.ignore_end) break; /*other errors may still happen though*/
- CERROR_BREAK(state->error, 63);
- }
-
- if(pos + (size_t)chunkLength + 12 > insize || pos + (size_t)chunkLength + 12 < pos) {
- CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk (or int overflow)*/
- }
-
- data = lodepng_chunk_data_const(chunk);
-
- unknown = 0;
-
- /*IDAT chunk, containing compressed image data*/
- if(lodepng_chunk_type_equals(chunk, "IDAT")) {
- size_t newsize;
- if(lodepng_addofl(idatsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95);
- if(newsize > insize) CERROR_BREAK(state->error, 95);
- lodepng_memcpy(idat + idatsize, data, chunkLength);
- idatsize += chunkLength;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- critical_pos = 3;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
- } else if(lodepng_chunk_type_equals(chunk, "IEND")) {
- /*IEND chunk*/
- IEND = 1;
- } else if(lodepng_chunk_type_equals(chunk, "PLTE")) {
- /*palette chunk (PLTE)*/
- state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength);
- if(state->error) break;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- critical_pos = 2;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
- } else if(lodepng_chunk_type_equals(chunk, "tRNS")) {
- /*palette transparency chunk (tRNS). Even though this one is an ancillary chunk , it is still compiled
- in without 'LODEPNG_COMPILE_ANCILLARY_CHUNKS' because it contains essential color information that
- affects the alpha channel of pixels. */
- state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength);
- if(state->error) break;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- /*background color chunk (bKGD)*/
- } else if(lodepng_chunk_type_equals(chunk, "bKGD")) {
- state->error = readChunk_bKGD(&state->info_png, data, chunkLength);
- if(state->error) break;
- } else if(lodepng_chunk_type_equals(chunk, "tEXt")) {
- /*text chunk (tEXt)*/
- if(state->decoder.read_text_chunks) {
- state->error = readChunk_tEXt(&state->info_png, data, chunkLength);
- if(state->error) break;
- }
- } else if(lodepng_chunk_type_equals(chunk, "zTXt")) {
- /*compressed text chunk (zTXt)*/
- if(state->decoder.read_text_chunks) {
- state->error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength);
- if(state->error) break;
- }
- } else if(lodepng_chunk_type_equals(chunk, "iTXt")) {
- /*international text chunk (iTXt)*/
- if(state->decoder.read_text_chunks) {
- state->error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength);
- if(state->error) break;
- }
- } else if(lodepng_chunk_type_equals(chunk, "tIME")) {
- state->error = readChunk_tIME(&state->info_png, data, chunkLength);
- if(state->error) break;
- } else if(lodepng_chunk_type_equals(chunk, "pHYs")) {
- state->error = readChunk_pHYs(&state->info_png, data, chunkLength);
- if(state->error) break;
- } else if(lodepng_chunk_type_equals(chunk, "gAMA")) {
- state->error = readChunk_gAMA(&state->info_png, data, chunkLength);
- if(state->error) break;
- } else if(lodepng_chunk_type_equals(chunk, "cHRM")) {
- state->error = readChunk_cHRM(&state->info_png, data, chunkLength);
- if(state->error) break;
- } else if(lodepng_chunk_type_equals(chunk, "sRGB")) {
- state->error = readChunk_sRGB(&state->info_png, data, chunkLength);
- if(state->error) break;
- } else if(lodepng_chunk_type_equals(chunk, "iCCP")) {
- state->error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength);
- if(state->error) break;
- } else if(lodepng_chunk_type_equals(chunk, "sBIT")) {
- state->error = readChunk_sBIT(&state->info_png, data, chunkLength);
- if(state->error) break;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
- } else /*it's not an implemented chunk type, so ignore it: skip over the data*/ {
- /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/
- if(!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) {
- CERROR_BREAK(state->error, 69);
- }
-
- unknown = 1;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- if(state->decoder.remember_unknown_chunks) {
- state->error = lodepng_chunk_append(&state->info_png.unknown_chunks_data[critical_pos - 1],
- &state->info_png.unknown_chunks_size[critical_pos - 1], chunk);
- if(state->error) break;
- }
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
- }
-
- if(!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ {
- if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/
- }
-
- if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize);
- }
-
- if(!state->error && state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) {
- state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */
- }
-
- if(!state->error) {
- /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation.
- If the decompressed size does not match the prediction, the image must be corrupt.*/
- if(state->info_png.interlace_method == 0) {
- size_t bpp = lodepng_get_bpp(&state->info_png.color);
- expected_size = lodepng_get_raw_size_idat(*w, *h, bpp);
- } else {
- size_t bpp = lodepng_get_bpp(&state->info_png.color);
- /*Adam-7 interlaced: expected size is the sum of the 7 sub-images sizes*/
- expected_size = 0;
- expected_size += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, bpp);
- if(*w > 4) expected_size += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, bpp);
- expected_size += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, bpp);
- if(*w > 2) expected_size += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, bpp);
- expected_size += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, bpp);
- if(*w > 1) expected_size += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, bpp);
- expected_size += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, bpp);
- }
-
- state->error = zlib_decompress(&scanlines, &scanlines_size, expected_size, idat, idatsize, &state->decoder.zlibsettings);
- }
- if(!state->error && scanlines_size != expected_size) state->error = 91; /*decompressed size doesn't match prediction*/
- lodepng_free(idat);
-
- if(!state->error) {
- outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color);
- *out = (unsigned char*)lodepng_malloc(outsize);
- if(!*out) state->error = 83; /*alloc fail*/
- }
- if(!state->error) {
- lodepng_memset(*out, 0, outsize);
- state->error = postProcessScanlines(*out, scanlines, *w, *h, &state->info_png);
- }
- lodepng_free(scanlines);
-}
-
-unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h,
- LodePNGState* state,
- const unsigned char* in, size_t insize) {
- *out = 0;
- decodeGeneric(out, w, h, state, in, insize);
- if(state->error) return state->error;
- if(!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) {
- /*same color type, no copying or converting of data needed*/
- /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype
- the raw image has to the end user*/
- if(!state->decoder.color_convert) {
- state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color);
- if(state->error) return state->error;
- }
- } else { /*color conversion needed*/
- unsigned char* data = *out;
- size_t outsize;
-
- /*TODO: check if this works according to the statement in the documentation: "The converter can convert
- from grayscale input color type, to 8-bit grayscale or grayscale with alpha"*/
- if(!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA)
- && !(state->info_raw.bitdepth == 8)) {
- return 56; /*unsupported color mode conversion*/
- }
-
- outsize = lodepng_get_raw_size(*w, *h, &state->info_raw);
- *out = (unsigned char*)lodepng_malloc(outsize);
- if(!(*out)) {
- state->error = 83; /*alloc fail*/
- }
- else state->error = lodepng_convert(*out, data, &state->info_raw,
- &state->info_png.color, *w, *h);
- lodepng_free(data);
- }
- return state->error;
-}
-
-unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in,
- size_t insize, LodePNGColorType colortype, unsigned bitdepth) {
- unsigned error;
- LodePNGState state;
- lodepng_state_init(&state);
- state.info_raw.colortype = colortype;
- state.info_raw.bitdepth = bitdepth;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- /*disable reading things that this function doesn't output*/
- state.decoder.read_text_chunks = 0;
- state.decoder.remember_unknown_chunks = 0;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
- error = lodepng_decode(out, w, h, &state, in, insize);
- lodepng_state_cleanup(&state);
- return error;
-}
-
-unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) {
- return lodepng_decode_memory(out, w, h, in, insize, LCT_RGBA, 8);
-}
-
-unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) {
- return lodepng_decode_memory(out, w, h, in, insize, LCT_RGB, 8);
-}
-
-#ifdef LODEPNG_COMPILE_DISK
-unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename,
- LodePNGColorType colortype, unsigned bitdepth) {
- unsigned char* buffer = 0;
- size_t buffersize;
- unsigned error;
- /* safe output values in case error happens */
- *out = 0;
- *w = *h = 0;
- error = lodepng_load_file(&buffer, &buffersize, filename);
- if(!error) error = lodepng_decode_memory(out, w, h, buffer, buffersize, colortype, bitdepth);
- lodepng_free(buffer);
- return error;
-}
-
-unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) {
- return lodepng_decode_file(out, w, h, filename, LCT_RGBA, 8);
-}
-
-unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) {
- return lodepng_decode_file(out, w, h, filename, LCT_RGB, 8);
-}
-#endif /*LODEPNG_COMPILE_DISK*/
-
-void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) {
- settings->color_convert = 1;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- settings->read_text_chunks = 1;
- settings->remember_unknown_chunks = 0;
- settings->max_text_size = 16777216;
- settings->max_icc_size = 16777216; /* 16MB is much more than enough for any reasonable ICC profile */
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
- settings->ignore_crc = 0;
- settings->ignore_critical = 0;
- settings->ignore_end = 0;
- lodepng_decompress_settings_init(&settings->zlibsettings);
-}
-
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER)
-
-void lodepng_state_init(LodePNGState* state) {
-#ifdef LODEPNG_COMPILE_DECODER
- lodepng_decoder_settings_init(&state->decoder);
-#endif /*LODEPNG_COMPILE_DECODER*/
-#ifdef LODEPNG_COMPILE_ENCODER
- lodepng_encoder_settings_init(&state->encoder);
-#endif /*LODEPNG_COMPILE_ENCODER*/
- lodepng_color_mode_init(&state->info_raw);
- lodepng_info_init(&state->info_png);
- state->error = 1;
-}
-
-void lodepng_state_cleanup(LodePNGState* state) {
- lodepng_color_mode_cleanup(&state->info_raw);
- lodepng_info_cleanup(&state->info_png);
-}
-
-void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source) {
- lodepng_state_cleanup(dest);
- *dest = *source;
- lodepng_color_mode_init(&dest->info_raw);
- lodepng_info_init(&dest->info_png);
- dest->error = lodepng_color_mode_copy(&dest->info_raw, &source->info_raw); if(dest->error) return;
- dest->error = lodepng_info_copy(&dest->info_png, &source->info_png); if(dest->error) return;
-}
-
-#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */
-
-#ifdef LODEPNG_COMPILE_ENCODER
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* / PNG Encoder / */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-
-static unsigned writeSignature(ucvector* out) {
- size_t pos = out->size;
- const unsigned char signature[] = {137, 80, 78, 71, 13, 10, 26, 10};
- /*8 bytes PNG signature, aka the magic bytes*/
- if(!ucvector_resize(out, out->size + 8)) return 83; /*alloc fail*/
- lodepng_memcpy(out->data + pos, signature, 8);
- return 0;
-}
-
-static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h,
- LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) {
- unsigned char *chunk, *data;
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 13, "IHDR"));
- data = chunk + 8;
-
- lodepng_set32bitInt(data + 0, w); /*width*/
- lodepng_set32bitInt(data + 4, h); /*height*/
- data[8] = (unsigned char)bitdepth; /*bit depth*/
- data[9] = (unsigned char)colortype; /*color type*/
- data[10] = 0; /*compression method*/
- data[11] = 0; /*filter method*/
- data[12] = interlace_method; /*interlace method*/
-
- lodepng_chunk_generate_crc(chunk);
- return 0;
-}
-
-/* only adds the chunk if needed (there is a key or palette with alpha) */
-static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) {
- unsigned char* chunk;
- size_t i, j = 8;
-
- if(info->palettesize == 0 || info->palettesize > 256) {
- return 68; /*invalid palette size, it is only allowed to be 1-256*/
- }
-
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, info->palettesize * 3, "PLTE"));
-
- for(i = 0; i != info->palettesize; ++i) {
- /*add all channels except alpha channel*/
- chunk[j++] = info->palette[i * 4 + 0];
- chunk[j++] = info->palette[i * 4 + 1];
- chunk[j++] = info->palette[i * 4 + 2];
- }
-
- lodepng_chunk_generate_crc(chunk);
- return 0;
-}
-
-static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) {
- unsigned char* chunk = 0;
-
- if(info->colortype == LCT_PALETTE) {
- size_t i, amount = info->palettesize;
- /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/
- for(i = info->palettesize; i != 0; --i) {
- if(info->palette[4 * (i - 1) + 3] != 255) break;
- --amount;
- }
- if(amount) {
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, amount, "tRNS"));
- /*add the alpha channel values from the palette*/
- for(i = 0; i != amount; ++i) chunk[8 + i] = info->palette[4 * i + 3];
- }
- } else if(info->colortype == LCT_GREY) {
- if(info->key_defined) {
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 2, "tRNS"));
- chunk[8] = (unsigned char)(info->key_r >> 8);
- chunk[9] = (unsigned char)(info->key_r & 255);
- }
- } else if(info->colortype == LCT_RGB) {
- if(info->key_defined) {
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 6, "tRNS"));
- chunk[8] = (unsigned char)(info->key_r >> 8);
- chunk[9] = (unsigned char)(info->key_r & 255);
- chunk[10] = (unsigned char)(info->key_g >> 8);
- chunk[11] = (unsigned char)(info->key_g & 255);
- chunk[12] = (unsigned char)(info->key_b >> 8);
- chunk[13] = (unsigned char)(info->key_b & 255);
- }
- }
-
- if(chunk) lodepng_chunk_generate_crc(chunk);
- return 0;
-}
-
-static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize,
- LodePNGCompressSettings* zlibsettings) {
- unsigned error = 0;
- unsigned char* zlib = 0;
- size_t zlibsize = 0;
-
- error = zlib_compress(&zlib, &zlibsize, data, datasize, zlibsettings);
- if(!error) {
- error = lodepng_chunk_createv(out, zlibsize, "IDAT", zlib);
- }
- lodepng_free(zlib);
- return error;
-}
-
-static unsigned addChunk_IEND(ucvector* out) {
- return lodepng_chunk_createv(out, 0, "IEND", 0);
-}
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-
-static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) {
- unsigned char* chunk = 0;
- size_t keysize = lodepng_strlen(keyword), textsize = lodepng_strlen(textstring);
- size_t size = keysize + 1 + textsize;
- if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, size, "tEXt"));
- lodepng_memcpy(chunk + 8, keyword, keysize);
- chunk[8 + keysize] = 0; /*null termination char*/
- lodepng_memcpy(chunk + 9 + keysize, textstring, textsize);
- lodepng_chunk_generate_crc(chunk);
- return 0;
-}
-
-static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring,
- LodePNGCompressSettings* zlibsettings) {
- unsigned error = 0;
- unsigned char* chunk = 0;
- unsigned char* compressed = 0;
- size_t compressedsize = 0;
- size_t textsize = lodepng_strlen(textstring);
- size_t keysize = lodepng_strlen(keyword);
- if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/
-
- error = zlib_compress(&compressed, &compressedsize,
- (const unsigned char*)textstring, textsize, zlibsettings);
- if(!error) {
- size_t size = keysize + 2 + compressedsize;
- error = lodepng_chunk_init(&chunk, out, size, "zTXt");
- }
- if(!error) {
- lodepng_memcpy(chunk + 8, keyword, keysize);
- chunk[8 + keysize] = 0; /*null termination char*/
- chunk[9 + keysize] = 0; /*compression method: 0*/
- lodepng_memcpy(chunk + 10 + keysize, compressed, compressedsize);
- lodepng_chunk_generate_crc(chunk);
- }
-
- lodepng_free(compressed);
- return error;
-}
-
-static unsigned addChunk_iTXt(ucvector* out, unsigned compress, const char* keyword, const char* langtag,
- const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) {
- unsigned error = 0;
- unsigned char* chunk = 0;
- unsigned char* compressed = 0;
- size_t compressedsize = 0;
- size_t textsize = lodepng_strlen(textstring);
- size_t keysize = lodepng_strlen(keyword), langsize = lodepng_strlen(langtag), transsize = lodepng_strlen(transkey);
-
- if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/
-
- if(compress) {
- error = zlib_compress(&compressed, &compressedsize,
- (const unsigned char*)textstring, textsize, zlibsettings);
- }
- if(!error) {
- size_t size = keysize + 3 + langsize + 1 + transsize + 1 + (compress ? compressedsize : textsize);
- error = lodepng_chunk_init(&chunk, out, size, "iTXt");
- }
- if(!error) {
- size_t pos = 8;
- lodepng_memcpy(chunk + pos, keyword, keysize);
- pos += keysize;
- chunk[pos++] = 0; /*null termination char*/
- chunk[pos++] = (compress ? 1 : 0); /*compression flag*/
- chunk[pos++] = 0; /*compression method: 0*/
- lodepng_memcpy(chunk + pos, langtag, langsize);
- pos += langsize;
- chunk[pos++] = 0; /*null termination char*/
- lodepng_memcpy(chunk + pos, transkey, transsize);
- pos += transsize;
- chunk[pos++] = 0; /*null termination char*/
- if(compress) {
- lodepng_memcpy(chunk + pos, compressed, compressedsize);
- } else {
- lodepng_memcpy(chunk + pos, textstring, textsize);
- }
- lodepng_chunk_generate_crc(chunk);
- }
-
- lodepng_free(compressed);
- return error;
-}
-
-static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) {
- unsigned char* chunk = 0;
- if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) {
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 2, "bKGD"));
- chunk[8] = (unsigned char)(info->background_r >> 8);
- chunk[9] = (unsigned char)(info->background_r & 255);
- } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) {
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 6, "bKGD"));
- chunk[8] = (unsigned char)(info->background_r >> 8);
- chunk[9] = (unsigned char)(info->background_r & 255);
- chunk[10] = (unsigned char)(info->background_g >> 8);
- chunk[11] = (unsigned char)(info->background_g & 255);
- chunk[12] = (unsigned char)(info->background_b >> 8);
- chunk[13] = (unsigned char)(info->background_b & 255);
- } else if(info->color.colortype == LCT_PALETTE) {
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 1, "bKGD"));
- chunk[8] = (unsigned char)(info->background_r & 255); /*palette index*/
- }
- if(chunk) lodepng_chunk_generate_crc(chunk);
- return 0;
-}
-
-static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) {
- unsigned char* chunk;
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 7, "tIME"));
- chunk[8] = (unsigned char)(time->year >> 8);
- chunk[9] = (unsigned char)(time->year & 255);
- chunk[10] = (unsigned char)time->month;
- chunk[11] = (unsigned char)time->day;
- chunk[12] = (unsigned char)time->hour;
- chunk[13] = (unsigned char)time->minute;
- chunk[14] = (unsigned char)time->second;
- lodepng_chunk_generate_crc(chunk);
- return 0;
-}
-
-static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) {
- unsigned char* chunk;
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 9, "pHYs"));
- lodepng_set32bitInt(chunk + 8, info->phys_x);
- lodepng_set32bitInt(chunk + 12, info->phys_y);
- chunk[16] = info->phys_unit;
- lodepng_chunk_generate_crc(chunk);
- return 0;
-}
-
-static unsigned addChunk_gAMA(ucvector* out, const LodePNGInfo* info) {
- unsigned char* chunk;
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 4, "gAMA"));
- lodepng_set32bitInt(chunk + 8, info->gama_gamma);
- lodepng_chunk_generate_crc(chunk);
- return 0;
-}
-
-static unsigned addChunk_cHRM(ucvector* out, const LodePNGInfo* info) {
- unsigned char* chunk;
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 32, "cHRM"));
- lodepng_set32bitInt(chunk + 8, info->chrm_white_x);
- lodepng_set32bitInt(chunk + 12, info->chrm_white_y);
- lodepng_set32bitInt(chunk + 16, info->chrm_red_x);
- lodepng_set32bitInt(chunk + 20, info->chrm_red_y);
- lodepng_set32bitInt(chunk + 24, info->chrm_green_x);
- lodepng_set32bitInt(chunk + 28, info->chrm_green_y);
- lodepng_set32bitInt(chunk + 32, info->chrm_blue_x);
- lodepng_set32bitInt(chunk + 36, info->chrm_blue_y);
- lodepng_chunk_generate_crc(chunk);
- return 0;
-}
-
-static unsigned addChunk_sRGB(ucvector* out, const LodePNGInfo* info) {
- unsigned char data = info->srgb_intent;
- return lodepng_chunk_createv(out, 1, "sRGB", &data);
-}
-
-static unsigned addChunk_iCCP(ucvector* out, const LodePNGInfo* info, LodePNGCompressSettings* zlibsettings) {
- unsigned error = 0;
- unsigned char* chunk = 0;
- unsigned char* compressed = 0;
- size_t compressedsize = 0;
- size_t keysize = lodepng_strlen(info->iccp_name);
-
- if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/
- error = zlib_compress(&compressed, &compressedsize,
- info->iccp_profile, info->iccp_profile_size, zlibsettings);
- if(!error) {
- size_t size = keysize + 2 + compressedsize;
- error = lodepng_chunk_init(&chunk, out, size, "iCCP");
- }
- if(!error) {
- lodepng_memcpy(chunk + 8, info->iccp_name, keysize);
- chunk[8 + keysize] = 0; /*null termination char*/
- chunk[9 + keysize] = 0; /*compression method: 0*/
- lodepng_memcpy(chunk + 10 + keysize, compressed, compressedsize);
- lodepng_chunk_generate_crc(chunk);
- }
-
- lodepng_free(compressed);
- return error;
-}
-
-static unsigned addChunk_sBIT(ucvector* out, const LodePNGInfo* info) {
- unsigned bitdepth = (info->color.colortype == LCT_PALETTE) ? 8 : info->color.bitdepth;
- unsigned char* chunk = 0;
- if(info->color.colortype == LCT_GREY) {
- if(info->sbit_r == 0 || info->sbit_r > bitdepth) return 115;
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 1, "sBIT"));
- chunk[8] = info->sbit_r;
- } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_PALETTE) {
- if(info->sbit_r == 0 || info->sbit_g == 0 || info->sbit_b == 0) return 115;
- if(info->sbit_r > bitdepth || info->sbit_g > bitdepth || info->sbit_b > bitdepth) return 115;
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 3, "sBIT"));
- chunk[8] = info->sbit_r;
- chunk[9] = info->sbit_g;
- chunk[10] = info->sbit_b;
- } else if(info->color.colortype == LCT_GREY_ALPHA) {
- if(info->sbit_r == 0 || info->sbit_a == 0) return 115;
- if(info->sbit_r > bitdepth || info->sbit_a > bitdepth) return 115;
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 2, "sBIT"));
- chunk[8] = info->sbit_r;
- chunk[9] = info->sbit_a;
- } else if(info->color.colortype == LCT_RGBA) {
- if(info->sbit_r == 0 || info->sbit_g == 0 || info->sbit_b == 0 || info->sbit_a == 0 ||
- info->sbit_r > bitdepth || info->sbit_g > bitdepth ||
- info->sbit_b > bitdepth || info->sbit_a > bitdepth) {
- return 115;
- }
- CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 4, "sBIT"));
- chunk[8] = info->sbit_r;
- chunk[9] = info->sbit_g;
- chunk[10] = info->sbit_b;
- chunk[11] = info->sbit_a;
- }
- if(chunk) lodepng_chunk_generate_crc(chunk);
- return 0;
-}
-
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline,
- size_t length, size_t bytewidth, unsigned char filterType) {
- size_t i;
- switch(filterType) {
- case 0: /*None*/
- for(i = 0; i != length; ++i) out[i] = scanline[i];
- break;
- case 1: /*Sub*/
- for(i = 0; i != bytewidth; ++i) out[i] = scanline[i];
- for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth];
- break;
- case 2: /*Up*/
- if(prevline) {
- for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i];
- } else {
- for(i = 0; i != length; ++i) out[i] = scanline[i];
- }
- break;
- case 3: /*Average*/
- if(prevline) {
- for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1);
- for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1);
- } else {
- for(i = 0; i != bytewidth; ++i) out[i] = scanline[i];
- for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1);
- }
- break;
- case 4: /*Paeth*/
- if(prevline) {
- /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/
- for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]);
- for(i = bytewidth; i < length; ++i) {
- out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth]));
- }
- } else {
- for(i = 0; i != bytewidth; ++i) out[i] = scanline[i];
- /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/
- for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]);
- }
- break;
- default: return; /*invalid filter type given*/
- }
-}
-
-/* integer binary logarithm, max return value is 31 */
-static size_t ilog2(size_t i) {
- size_t result = 0;
- if(i >= 65536) { result += 16; i >>= 16; }
- if(i >= 256) { result += 8; i >>= 8; }
- if(i >= 16) { result += 4; i >>= 4; }
- if(i >= 4) { result += 2; i >>= 2; }
- if(i >= 2) { result += 1; /*i >>= 1;*/ }
- return result;
-}
-
-/* integer approximation for i * log2(i), helper function for LFS_ENTROPY */
-static size_t ilog2i(size_t i) {
- size_t l;
- if(i == 0) return 0;
- l = ilog2(i);
- /* approximate i*log2(i): l is integer logarithm, ((i - (1u << l)) << 1u)
- linearly approximates the missing fractional part multiplied by i */
- return i * l + ((i - (1u << l)) << 1u);
-}
-
-static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h,
- const LodePNGColorMode* color, const LodePNGEncoderSettings* settings) {
- /*
- For PNG filter method 0
- out must be a buffer with as size: h + (w * h * bpp + 7u) / 8u, because there are
- the scanlines with 1 extra byte per scanline
- */
-
- unsigned bpp = lodepng_get_bpp(color);
- /*the width of a scanline in bytes, not including the filter type*/
- size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u;
-
- /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
- size_t bytewidth = (bpp + 7u) / 8u;
- const unsigned char* prevline = 0;
- unsigned x, y;
- unsigned error = 0;
- LodePNGFilterStrategy strategy = settings->filter_strategy;
-
- /*
- There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard:
- * If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e.
- use fixed filtering, with the filter None).
- * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is
- not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply
- all five filters and select the filter that produces the smallest sum of absolute values per row.
- This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true.
-
- If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed,
- but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum
- heuristic is used.
- */
- if(settings->filter_palette_zero &&
- (color->colortype == LCT_PALETTE || color->bitdepth < 8)) strategy = LFS_ZERO;
-
- if(bpp == 0) return 31; /*error: invalid color type*/
-
- if(strategy >= LFS_ZERO && strategy <= LFS_FOUR) {
- unsigned char type = (unsigned char)strategy;
- for(y = 0; y != h; ++y) {
- size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
- size_t inindex = linebytes * y;
- out[outindex] = type; /*filter type byte*/
- filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type);
- prevline = &in[inindex];
- }
- } else if(strategy == LFS_MINSUM) {
- /*adaptive filtering*/
- unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/
- size_t smallest = 0;
- unsigned char type, bestType = 0;
-
- for(type = 0; type != 5; ++type) {
- attempt[type] = (unsigned char*)lodepng_malloc(linebytes);
- if(!attempt[type]) error = 83; /*alloc fail*/
- }
-
- if(!error) {
- for(y = 0; y != h; ++y) {
- /*try the 5 filter types*/
- for(type = 0; type != 5; ++type) {
- size_t sum = 0;
- filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
-
- /*calculate the sum of the result*/
- if(type == 0) {
- for(x = 0; x != linebytes; ++x) sum += (unsigned char)(attempt[type][x]);
- } else {
- for(x = 0; x != linebytes; ++x) {
- /*For differences, each byte should be treated as signed, values above 127 are negative
- (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there.
- This means filtertype 0 is almost never chosen, but that is justified.*/
- unsigned char s = attempt[type][x];
- sum += s < 128 ? s : (255U - s);
- }
- }
-
- /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/
- if(type == 0 || sum < smallest) {
- bestType = type;
- smallest = sum;
- }
- }
-
- prevline = &in[y * linebytes];
-
- /*now fill the out values*/
- out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
- for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
- }
- }
-
- for(type = 0; type != 5; ++type) lodepng_free(attempt[type]);
- } else if(strategy == LFS_ENTROPY) {
- unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/
- size_t bestSum = 0;
- unsigned type, bestType = 0;
- unsigned count[256];
-
- for(type = 0; type != 5; ++type) {
- attempt[type] = (unsigned char*)lodepng_malloc(linebytes);
- if(!attempt[type]) error = 83; /*alloc fail*/
- }
-
- if(!error) {
- for(y = 0; y != h; ++y) {
- /*try the 5 filter types*/
- for(type = 0; type != 5; ++type) {
- size_t sum = 0;
- filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
- lodepng_memset(count, 0, 256 * sizeof(*count));
- for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]];
- ++count[type]; /*the filter type itself is part of the scanline*/
- for(x = 0; x != 256; ++x) {
- sum += ilog2i(count[x]);
- }
- /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/
- if(type == 0 || sum > bestSum) {
- bestType = type;
- bestSum = sum;
- }
- }
-
- prevline = &in[y * linebytes];
-
- /*now fill the out values*/
- out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
- for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
- }
- }
-
- for(type = 0; type != 5; ++type) lodepng_free(attempt[type]);
- } else if(strategy == LFS_PREDEFINED) {
- for(y = 0; y != h; ++y) {
- size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
- size_t inindex = linebytes * y;
- unsigned char type = settings->predefined_filters[y];
- out[outindex] = type; /*filter type byte*/
- filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type);
- prevline = &in[inindex];
- }
- } else if(strategy == LFS_BRUTE_FORCE) {
- /*brute force filter chooser.
- deflate the scanline after every filter attempt to see which one deflates best.
- This is very slow and gives only slightly smaller, sometimes even larger, result*/
- size_t size[5];
- unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/
- size_t smallest = 0;
- unsigned type = 0, bestType = 0;
- unsigned char* dummy;
- LodePNGCompressSettings zlibsettings;
- lodepng_memcpy(&zlibsettings, &settings->zlibsettings, sizeof(LodePNGCompressSettings));
- /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose,
- to simulate the true case where the tree is the same for the whole image. Sometimes it gives
- better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare
- cases better compression. It does make this a bit less slow, so it's worth doing this.*/
- zlibsettings.btype = 1;
- /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG
- images only, so disable it*/
- zlibsettings.custom_zlib = 0;
- zlibsettings.custom_deflate = 0;
- for(type = 0; type != 5; ++type) {
- attempt[type] = (unsigned char*)lodepng_malloc(linebytes);
- if(!attempt[type]) error = 83; /*alloc fail*/
- }
- if(!error) {
- for(y = 0; y != h; ++y) /*try the 5 filter types*/ {
- for(type = 0; type != 5; ++type) {
- unsigned testsize = (unsigned)linebytes;
- /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/
-
- filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
- size[type] = 0;
- dummy = 0;
- zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings);
- lodepng_free(dummy);
- /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/
- if(type == 0 || size[type] < smallest) {
- bestType = type;
- smallest = size[type];
- }
- }
- prevline = &in[y * linebytes];
- out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
- for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
- }
- }
- for(type = 0; type != 5; ++type) lodepng_free(attempt[type]);
- }
- else return 88; /* unknown filter strategy */
-
- return error;
-}
-
-static void addPaddingBits(unsigned char* out, const unsigned char* in,
- size_t olinebits, size_t ilinebits, unsigned h) {
- /*The opposite of the removePaddingBits function
- olinebits must be >= ilinebits*/
- unsigned y;
- size_t diff = olinebits - ilinebits;
- size_t obp = 0, ibp = 0; /*bit pointers*/
- for(y = 0; y != h; ++y) {
- size_t x;
- for(x = 0; x < ilinebits; ++x) {
- unsigned char bit = readBitFromReversedStream(&ibp, in);
- setBitOfReversedStream(&obp, out, bit);
- }
- /*obp += diff; --> no, fill in some value in the padding bits too, to avoid
- "Use of uninitialised value of size ###" warning from valgrind*/
- for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0);
- }
-}
-
-/*
-in: non-interlaced image with size w*h
-out: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with
- no padding bits between scanlines, but between reduced images so that each
- reduced image starts at a byte.
-bpp: bits per pixel
-there are no padding bits, not between scanlines, not between reduced images
-in has the following size in bits: w * h * bpp.
-out is possibly bigger due to padding bits between reduced images
-NOTE: comments about padding bits are only relevant if bpp < 8
-*/
-static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) {
- unsigned passw[7], passh[7];
- size_t filter_passstart[8], padded_passstart[8], passstart[8];
- unsigned i;
-
- Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
-
- if(bpp >= 8) {
- for(i = 0; i != 7; ++i) {
- unsigned x, y, b;
- size_t bytewidth = bpp / 8u;
- for(y = 0; y < passh[i]; ++y)
- for(x = 0; x < passw[i]; ++x) {
- size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth;
- size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth;
- for(b = 0; b < bytewidth; ++b) {
- out[pixeloutstart + b] = in[pixelinstart + b];
- }
- }
- }
- } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ {
- for(i = 0; i != 7; ++i) {
- unsigned x, y, b;
- unsigned ilinebits = bpp * passw[i];
- unsigned olinebits = bpp * w;
- size_t obp, ibp; /*bit pointers (for out and in buffer)*/
- for(y = 0; y < passh[i]; ++y)
- for(x = 0; x < passw[i]; ++x) {
- ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp;
- obp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
- for(b = 0; b < bpp; ++b) {
- unsigned char bit = readBitFromReversedStream(&ibp, in);
- setBitOfReversedStream(&obp, out, bit);
- }
- }
- }
- }
-}
-
-/*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image.
-return value is error**/
-static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in,
- unsigned w, unsigned h,
- const LodePNGInfo* info_png, const LodePNGEncoderSettings* settings) {
- /*
- This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps:
- *) if no Adam7: 1) add padding bits (= possible extra bits per scanline if bpp < 8) 2) filter
- *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter
- */
- unsigned bpp = lodepng_get_bpp(&info_png->color);
- unsigned error = 0;
-
- if(info_png->interlace_method == 0) {
- *outsize = h + (h * ((w * bpp + 7u) / 8u)); /*image size plus an extra byte per scanline + possible padding bits*/
- *out = (unsigned char*)lodepng_malloc(*outsize);
- if(!(*out) && (*outsize)) error = 83; /*alloc fail*/
-
- if(!error) {
- /*non multiple of 8 bits per scanline, padding bits needed per scanline*/
- if(bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) {
- unsigned char* padded = (unsigned char*)lodepng_malloc(h * ((w * bpp + 7u) / 8u));
- if(!padded) error = 83; /*alloc fail*/
- if(!error) {
- addPaddingBits(padded, in, ((w * bpp + 7u) / 8u) * 8u, w * bpp, h);
- error = filter(*out, padded, w, h, &info_png->color, settings);
- }
- lodepng_free(padded);
- } else {
- /*we can immediately filter into the out buffer, no other steps needed*/
- error = filter(*out, in, w, h, &info_png->color, settings);
- }
- }
- } else /*interlace_method is 1 (Adam7)*/ {
- unsigned passw[7], passh[7];
- size_t filter_passstart[8], padded_passstart[8], passstart[8];
- unsigned char* adam7;
-
- Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
-
- *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/
- *out = (unsigned char*)lodepng_malloc(*outsize);
- if(!(*out)) error = 83; /*alloc fail*/
-
- adam7 = (unsigned char*)lodepng_malloc(passstart[7]);
- if(!adam7 && passstart[7]) error = 83; /*alloc fail*/
-
- if(!error) {
- unsigned i;
-
- Adam7_interlace(adam7, in, w, h, bpp);
- for(i = 0; i != 7; ++i) {
- if(bpp < 8) {
- unsigned char* padded = (unsigned char*)lodepng_malloc(padded_passstart[i + 1] - padded_passstart[i]);
- if(!padded) ERROR_BREAK(83); /*alloc fail*/
- addPaddingBits(padded, &adam7[passstart[i]],
- ((passw[i] * bpp + 7u) / 8u) * 8u, passw[i] * bpp, passh[i]);
- error = filter(&(*out)[filter_passstart[i]], padded,
- passw[i], passh[i], &info_png->color, settings);
- lodepng_free(padded);
- } else {
- error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]],
- passw[i], passh[i], &info_png->color, settings);
- }
-
- if(error) break;
- }
- }
-
- lodepng_free(adam7);
- }
-
- return error;
-}
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) {
- unsigned char* inchunk = data;
- while((size_t)(inchunk - data) < datasize) {
- CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk));
- out->allocsize = out->size; /*fix the allocsize again*/
- inchunk = lodepng_chunk_next(inchunk, data + datasize);
- }
- return 0;
-}
-
-static unsigned isGrayICCProfile(const unsigned char* profile, unsigned size) {
- /*
- It is a gray profile if bytes 16-19 are "GRAY", rgb profile if bytes 16-19
- are "RGB ". We do not perform any full parsing of the ICC profile here, other
- than check those 4 bytes to grayscale profile. Other than that, validity of
- the profile is not checked. This is needed only because the PNG specification
- requires using a non-gray color model if there is an ICC profile with "RGB "
- (sadly limiting compression opportunities if the input data is grayscale RGB
- data), and requires using a gray color model if it is "GRAY".
- */
- if(size < 20) return 0;
- return profile[16] == 'G' && profile[17] == 'R' && profile[18] == 'A' && profile[19] == 'Y';
-}
-
-static unsigned isRGBICCProfile(const unsigned char* profile, unsigned size) {
- /* See comment in isGrayICCProfile*/
- if(size < 20) return 0;
- return profile[16] == 'R' && profile[17] == 'G' && profile[18] == 'B' && profile[19] == ' ';
-}
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-unsigned lodepng_encode(unsigned char** out, size_t* outsize,
- const unsigned char* image, unsigned w, unsigned h,
- LodePNGState* state) {
- unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/
- size_t datasize = 0;
- ucvector outv = ucvector_init(NULL, 0);
- LodePNGInfo info;
- const LodePNGInfo* info_png = &state->info_png;
- LodePNGColorMode auto_color;
-
- lodepng_info_init(&info);
- lodepng_color_mode_init(&auto_color);
-
- /*provide some proper output values if error will happen*/
- *out = 0;
- *outsize = 0;
- state->error = 0;
-
- /*check input values validity*/
- if((info_png->color.colortype == LCT_PALETTE || state->encoder.force_palette)
- && (info_png->color.palettesize == 0 || info_png->color.palettesize > 256)) {
- /*this error is returned even if auto_convert is enabled and thus encoder could
- generate the palette by itself: while allowing this could be possible in theory,
- it may complicate the code or edge cases, and always requiring to give a palette
- when setting this color type is a simpler contract*/
- state->error = 68; /*invalid palette size, it is only allowed to be 1-256*/
- goto cleanup;
- }
- if(state->encoder.zlibsettings.btype > 2) {
- state->error = 61; /*error: invalid btype*/
- goto cleanup;
- }
- if(info_png->interlace_method > 1) {
- state->error = 71; /*error: invalid interlace mode*/
- goto cleanup;
- }
- state->error = checkColorValidity(info_png->color.colortype, info_png->color.bitdepth);
- if(state->error) goto cleanup; /*error: invalid color type given*/
- state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth);
- if(state->error) goto cleanup; /*error: invalid color type given*/
-
- /* color convert and compute scanline filter types */
- lodepng_info_copy(&info, &state->info_png);
- if(state->encoder.auto_convert) {
- LodePNGColorStats stats;
- unsigned allow_convert = 1;
- lodepng_color_stats_init(&stats);
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- if(info_png->iccp_defined &&
- isGrayICCProfile(info_png->iccp_profile, info_png->iccp_profile_size)) {
- /*the PNG specification does not allow to use palette with a GRAY ICC profile, even
- if the palette has only gray colors, so disallow it.*/
- stats.allow_palette = 0;
- }
- if(info_png->iccp_defined &&
- isRGBICCProfile(info_png->iccp_profile, info_png->iccp_profile_size)) {
- /*the PNG specification does not allow to use grayscale color with RGB ICC profile, so disallow gray.*/
- stats.allow_greyscale = 0;
- }
-#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */
- state->error = lodepng_compute_color_stats(&stats, image, w, h, &state->info_raw);
- if(state->error) goto cleanup;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- if(info_png->background_defined) {
- /*the background chunk's color must be taken into account as well*/
- unsigned r = 0, g = 0, b = 0;
- LodePNGColorMode mode16 = lodepng_color_mode_make(LCT_RGB, 16);
- lodepng_convert_rgb(&r, &g, &b,
- info_png->background_r, info_png->background_g, info_png->background_b, &mode16, &info_png->color);
- state->error = lodepng_color_stats_add(&stats, r, g, b, 65535);
- if(state->error) goto cleanup;
- }
-#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */
- state->error = auto_choose_color(&auto_color, &state->info_raw, &stats);
- if(state->error) goto cleanup;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- if(info_png->sbit_defined) {
- /*if sbit is defined, due to strict requirements of which sbit values can be present for which color modes,
- auto_convert can't be done in many cases. However, do support a few cases here.
- TODO: more conversions may be possible, and it may also be possible to get a more appropriate color type out of
- auto_choose_color if knowledge about sbit is used beforehand
- */
- unsigned sbit_max = LODEPNG_MAX(LODEPNG_MAX(LODEPNG_MAX(info_png->sbit_r, info_png->sbit_g),
- info_png->sbit_b), info_png->sbit_a);
- unsigned equal = (!info_png->sbit_g || info_png->sbit_g == info_png->sbit_r)
- && (!info_png->sbit_b || info_png->sbit_b == info_png->sbit_r)
- && (!info_png->sbit_a || info_png->sbit_a == info_png->sbit_r);
- allow_convert = 0;
- if(info.color.colortype == LCT_PALETTE &&
- auto_color.colortype == LCT_PALETTE) {
- /* input and output are palette, and in this case it may happen that palette data is
- expected to be copied from info_raw into the info_png */
- allow_convert = 1;
- }
- /*going from 8-bit RGB to palette (or 16-bit as long as sbit_max <= 8) is possible
- since both are 8-bit RGB for sBIT's purposes*/
- if(info.color.colortype == LCT_RGB &&
- auto_color.colortype == LCT_PALETTE && sbit_max <= 8) {
- allow_convert = 1;
- }
- /*going from 8-bit RGBA to palette is also ok but only if sbit_a is exactly 8*/
- if(info.color.colortype == LCT_RGBA && auto_color.colortype == LCT_PALETTE &&
- info_png->sbit_a == 8 && sbit_max <= 8) {
- allow_convert = 1;
- }
- /*going from 16-bit RGB(A) to 8-bit RGB(A) is ok if all sbit values are <= 8*/
- if((info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA) && info.color.bitdepth == 16 &&
- auto_color.colortype == info.color.colortype && auto_color.bitdepth == 8 &&
- sbit_max <= 8) {
- allow_convert = 1;
- }
- /*going to less channels is ok if all bit values are equal (all possible values in sbit,
- as well as the chosen bitdepth of the result). Due to how auto_convert works,
- we already know that auto_color.colortype has less than or equal amount of channels than
- info.colortype. Palette is not used here. This conversion is not allowed if
- info_png->sbit_r < auto_color.bitdepth, because specifically for alpha, non-presence of
- an sbit value heavily implies that alpha's bit depth is equal to the PNG bit depth (rather
- than the bit depths set in the r, g and b sbit values, by how the PNG specification describes
- handling tRNS chunk case with sBIT), so be conservative here about ignoring user input.*/
- if(info.color.colortype != LCT_PALETTE && auto_color.colortype != LCT_PALETTE &&
- equal && info_png->sbit_r == auto_color.bitdepth) {
- allow_convert = 1;
- }
- }
-#endif
- if(state->encoder.force_palette) {
- if(info.color.colortype != LCT_GREY && info.color.colortype != LCT_GREY_ALPHA &&
- (auto_color.colortype == LCT_GREY || auto_color.colortype == LCT_GREY_ALPHA)) {
- /*user speficially forced a PLTE palette, so cannot convert to grayscale types because
- the PNG specification only allows writing a suggested palette in PLTE for truecolor types*/
- allow_convert = 0;
- }
- }
- if(allow_convert) {
- lodepng_color_mode_copy(&info.color, &auto_color);
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- /*also convert the background chunk*/
- if(info_png->background_defined) {
- if(lodepng_convert_rgb(&info.background_r, &info.background_g, &info.background_b,
- info_png->background_r, info_png->background_g, info_png->background_b, &info.color, &info_png->color)) {
- state->error = 104;
- goto cleanup;
- }
- }
-#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */
- }
- }
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- if(info_png->iccp_defined) {
- unsigned gray_icc = isGrayICCProfile(info_png->iccp_profile, info_png->iccp_profile_size);
- unsigned rgb_icc = isRGBICCProfile(info_png->iccp_profile, info_png->iccp_profile_size);
- unsigned gray_png = info.color.colortype == LCT_GREY || info.color.colortype == LCT_GREY_ALPHA;
- if(!gray_icc && !rgb_icc) {
- state->error = 100; /* Disallowed profile color type for PNG */
- goto cleanup;
- }
- if(gray_icc != gray_png) {
- /*Not allowed to use RGB/RGBA/palette with GRAY ICC profile or vice versa,
- or in case of auto_convert, it wasn't possible to find appropriate model*/
- state->error = state->encoder.auto_convert ? 102 : 101;
- goto cleanup;
- }
- }
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
- if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) {
- unsigned char* converted;
- size_t size = ((size_t)w * (size_t)h * (size_t)lodepng_get_bpp(&info.color) + 7u) / 8u;
-
- converted = (unsigned char*)lodepng_malloc(size);
- if(!converted && size) state->error = 83; /*alloc fail*/
- if(!state->error) {
- state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h);
- }
- if(!state->error) {
- state->error = preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder);
- }
- lodepng_free(converted);
- if(state->error) goto cleanup;
- } else {
- state->error = preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder);
- if(state->error) goto cleanup;
- }
-
- /* output all PNG chunks */ {
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- size_t i;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
- /*write signature and chunks*/
- state->error = writeSignature(&outv);
- if(state->error) goto cleanup;
- /*IHDR*/
- state->error = addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method);
- if(state->error) goto cleanup;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- /*unknown chunks between IHDR and PLTE*/
- if(info.unknown_chunks_data[0]) {
- state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]);
- if(state->error) goto cleanup;
- }
- /*color profile chunks must come before PLTE */
- if(info.iccp_defined) {
- state->error = addChunk_iCCP(&outv, &info, &state->encoder.zlibsettings);
- if(state->error) goto cleanup;
- }
- if(info.srgb_defined) {
- state->error = addChunk_sRGB(&outv, &info);
- if(state->error) goto cleanup;
- }
- if(info.gama_defined) {
- state->error = addChunk_gAMA(&outv, &info);
- if(state->error) goto cleanup;
- }
- if(info.chrm_defined) {
- state->error = addChunk_cHRM(&outv, &info);
- if(state->error) goto cleanup;
- }
- if(info_png->sbit_defined) {
- state->error = addChunk_sBIT(&outv, &info);
- if(state->error) goto cleanup;
- }
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
- /*PLTE*/
- if(info.color.colortype == LCT_PALETTE) {
- state->error = addChunk_PLTE(&outv, &info.color);
- if(state->error) goto cleanup;
- }
- if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) {
- /*force_palette means: write suggested palette for truecolor in PLTE chunk*/
- state->error = addChunk_PLTE(&outv, &info.color);
- if(state->error) goto cleanup;
- }
- /*tRNS (this will only add if when necessary) */
- state->error = addChunk_tRNS(&outv, &info.color);
- if(state->error) goto cleanup;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- /*bKGD (must come between PLTE and the IDAt chunks*/
- if(info.background_defined) {
- state->error = addChunk_bKGD(&outv, &info);
- if(state->error) goto cleanup;
- }
- /*pHYs (must come before the IDAT chunks)*/
- if(info.phys_defined) {
- state->error = addChunk_pHYs(&outv, &info);
- if(state->error) goto cleanup;
- }
-
- /*unknown chunks between PLTE and IDAT*/
- if(info.unknown_chunks_data[1]) {
- state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]);
- if(state->error) goto cleanup;
- }
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
- /*IDAT (multiple IDAT chunks must be consecutive)*/
- state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings);
- if(state->error) goto cleanup;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- /*tIME*/
- if(info.time_defined) {
- state->error = addChunk_tIME(&outv, &info.time);
- if(state->error) goto cleanup;
- }
- /*tEXt and/or zTXt*/
- for(i = 0; i != info.text_num; ++i) {
- if(lodepng_strlen(info.text_keys[i]) > 79) {
- state->error = 66; /*text chunk too large*/
- goto cleanup;
- }
- if(lodepng_strlen(info.text_keys[i]) < 1) {
- state->error = 67; /*text chunk too small*/
- goto cleanup;
- }
- if(state->encoder.text_compression) {
- state->error = addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings);
- if(state->error) goto cleanup;
- } else {
- state->error = addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]);
- if(state->error) goto cleanup;
- }
- }
- /*LodePNG version id in text chunk*/
- if(state->encoder.add_id) {
- unsigned already_added_id_text = 0;
- for(i = 0; i != info.text_num; ++i) {
- const char* k = info.text_keys[i];
- /* Could use strcmp, but we're not calling or reimplementing this C library function for this use only */
- if(k[0] == 'L' && k[1] == 'o' && k[2] == 'd' && k[3] == 'e' &&
- k[4] == 'P' && k[5] == 'N' && k[6] == 'G' && k[7] == '\0') {
- already_added_id_text = 1;
- break;
- }
- }
- if(already_added_id_text == 0) {
- state->error = addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/
- if(state->error) goto cleanup;
- }
- }
- /*iTXt*/
- for(i = 0; i != info.itext_num; ++i) {
- if(lodepng_strlen(info.itext_keys[i]) > 79) {
- state->error = 66; /*text chunk too large*/
- goto cleanup;
- }
- if(lodepng_strlen(info.itext_keys[i]) < 1) {
- state->error = 67; /*text chunk too small*/
- goto cleanup;
- }
- state->error = addChunk_iTXt(
- &outv, state->encoder.text_compression,
- info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i],
- &state->encoder.zlibsettings);
- if(state->error) goto cleanup;
- }
-
- /*unknown chunks between IDAT and IEND*/
- if(info.unknown_chunks_data[2]) {
- state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]);
- if(state->error) goto cleanup;
- }
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
- state->error = addChunk_IEND(&outv);
- if(state->error) goto cleanup;
- }
-
-cleanup:
- lodepng_info_cleanup(&info);
- lodepng_free(data);
- lodepng_color_mode_cleanup(&auto_color);
-
- /*instead of cleaning the vector up, give it to the output*/
- *out = outv.data;
- *outsize = outv.size;
-
- return state->error;
-}
-
-unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image,
- unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) {
- unsigned error;
- LodePNGState state;
- lodepng_state_init(&state);
- state.info_raw.colortype = colortype;
- state.info_raw.bitdepth = bitdepth;
- state.info_png.color.colortype = colortype;
- state.info_png.color.bitdepth = bitdepth;
- lodepng_encode(out, outsize, image, w, h, &state);
- error = state.error;
- lodepng_state_cleanup(&state);
- return error;
-}
-
-unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) {
- return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGBA, 8);
-}
-
-unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) {
- return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGB, 8);
-}
-
-#ifdef LODEPNG_COMPILE_DISK
-unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h,
- LodePNGColorType colortype, unsigned bitdepth) {
- unsigned char* buffer;
- size_t buffersize;
- unsigned error = lodepng_encode_memory(&buffer, &buffersize, image, w, h, colortype, bitdepth);
- if(!error) error = lodepng_save_file(buffer, buffersize, filename);
- lodepng_free(buffer);
- return error;
-}
-
-unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) {
- return lodepng_encode_file(filename, image, w, h, LCT_RGBA, 8);
-}
-
-unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) {
- return lodepng_encode_file(filename, image, w, h, LCT_RGB, 8);
-}
-#endif /*LODEPNG_COMPILE_DISK*/
-
-void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings) {
- lodepng_compress_settings_init(&settings->zlibsettings);
- settings->filter_palette_zero = 1;
- settings->filter_strategy = LFS_MINSUM;
- settings->auto_convert = 1;
- settings->force_palette = 0;
- settings->predefined_filters = 0;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- settings->add_id = 0;
- settings->text_compression = 1;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-}
-
-#endif /*LODEPNG_COMPILE_ENCODER*/
-#endif /*LODEPNG_COMPILE_PNG*/
-
-#ifdef LODEPNG_COMPILE_ERROR_TEXT
-/*
-This returns the description of a numerical error code in English. This is also
-the documentation of all the error codes.
-*/
-const char* lodepng_error_text(unsigned code) {
- switch(code) {
- case 0: return "no error, everything went ok";
- case 1: return "nothing done yet"; /*the Encoder/Decoder has done nothing yet, error checking makes no sense yet*/
- case 10: return "end of input memory reached without huffman end code"; /*while huffman decoding*/
- case 11: return "error in code tree made it jump outside of huffman tree"; /*while huffman decoding*/
- case 13: return "problem while processing dynamic deflate block";
- case 14: return "problem while processing dynamic deflate block";
- case 15: return "problem while processing dynamic deflate block";
- /*this error could happen if there are only 0 or 1 symbols present in the huffman code:*/
- case 16: return "invalid code while processing dynamic deflate block";
- case 17: return "end of out buffer memory reached while inflating";
- case 18: return "invalid distance code while inflating";
- case 19: return "end of out buffer memory reached while inflating";
- case 20: return "invalid deflate block BTYPE encountered while decoding";
- case 21: return "NLEN is not ones complement of LEN in a deflate block";
-
- /*end of out buffer memory reached while inflating:
- This can happen if the inflated deflate data is longer than the amount of bytes required to fill up
- all the pixels of the image, given the color depth and image dimensions. Something that doesn't
- happen in a normal, well encoded, PNG image.*/
- case 22: return "end of out buffer memory reached while inflating";
- case 23: return "end of in buffer memory reached while inflating";
- case 24: return "invalid FCHECK in zlib header";
- case 25: return "invalid compression method in zlib header";
- case 26: return "FDICT encountered in zlib header while it's not used for PNG";
- case 27: return "PNG file is smaller than a PNG header";
- /*Checks the magic file header, the first 8 bytes of the PNG file*/
- case 28: return "incorrect PNG signature, it's no PNG or corrupted";
- case 29: return "first chunk is not the header chunk";
- case 30: return "chunk length too large, chunk broken off at end of file";
- case 31: return "illegal PNG color type or bpp";
- case 32: return "illegal PNG compression method";
- case 33: return "illegal PNG filter method";
- case 34: return "illegal PNG interlace method";
- case 35: return "chunk length of a chunk is too large or the chunk too small";
- case 36: return "illegal PNG filter type encountered";
- case 37: return "illegal bit depth for this color type given";
- case 38: return "the palette is too small or too big"; /*0, or more than 256 colors*/
- case 39: return "tRNS chunk before PLTE or has more entries than palette size";
- case 40: return "tRNS chunk has wrong size for grayscale image";
- case 41: return "tRNS chunk has wrong size for RGB image";
- case 42: return "tRNS chunk appeared while it was not allowed for this color type";
- case 43: return "bKGD chunk has wrong size for palette image";
- case 44: return "bKGD chunk has wrong size for grayscale image";
- case 45: return "bKGD chunk has wrong size for RGB image";
- case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?";
- case 49: return "jumped past memory while generating dynamic huffman tree";
- case 50: return "jumped past memory while generating dynamic huffman tree";
- case 51: return "jumped past memory while inflating huffman block";
- case 52: return "jumped past memory while inflating";
- case 53: return "size of zlib data too small";
- case 54: return "repeat symbol in tree while there was no value symbol yet";
- /*jumped past tree while generating huffman tree, this could be when the
- tree will have more leaves than symbols after generating it out of the
- given lengths. They call this an oversubscribed dynamic bit lengths tree in zlib.*/
- case 55: return "jumped past tree while generating huffman tree";
- case 56: return "given output image colortype or bitdepth not supported for color conversion";
- case 57: return "invalid CRC encountered (checking CRC can be disabled)";
- case 58: return "invalid ADLER32 encountered (checking ADLER32 can be disabled)";
- case 59: return "requested color conversion not supported";
- case 60: return "invalid window size given in the settings of the encoder (must be 0-32768)";
- case 61: return "invalid BTYPE given in the settings of the encoder (only 0, 1 and 2 are allowed)";
- /*LodePNG leaves the choice of RGB to grayscale conversion formula to the user.*/
- case 62: return "conversion from color to grayscale not supported";
- /*(2^31-1)*/
- case 63: return "length of a chunk too long, max allowed for PNG is 2147483647 bytes per chunk";
- /*this would result in the inability of a deflated block to ever contain an end code. It must be at least 1.*/
- case 64: return "the length of the END symbol 256 in the Huffman tree is 0";
- case 66: return "the length of a text chunk keyword given to the encoder is longer than the maximum of 79 bytes";
- case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte";
- case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors";
- case 69: return "unknown chunk type with 'critical' flag encountered by the decoder";
- case 71: return "invalid interlace mode given to encoder (must be 0 or 1)";
- case 72: return "while decoding, invalid compression method encountering in zTXt or iTXt chunk (it must be 0)";
- case 73: return "invalid tIME chunk size";
- case 74: return "invalid pHYs chunk size";
- /*length could be wrong, or data chopped off*/
- case 75: return "no null termination char found while decoding text chunk";
- case 76: return "iTXt chunk too short to contain required bytes";
- case 77: return "integer overflow in buffer size";
- case 78: return "failed to open file for reading"; /*file doesn't exist or couldn't be opened for reading*/
- case 79: return "failed to open file for writing";
- case 80: return "tried creating a tree of 0 symbols";
- case 81: return "lazy matching at pos 0 is impossible";
- case 82: return "color conversion to palette requested while a color isn't in palette, or index out of bounds";
- case 83: return "memory allocation failed";
- case 84: return "given image too small to contain all pixels to be encoded";
- case 86: return "impossible offset in lz77 encoding (internal bug)";
- case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined";
- case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy";
- case 89: return "text chunk keyword too short or long: must have size 1-79";
- /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/
- case 90: return "windowsize must be a power of two";
- case 91: return "invalid decompressed idat size";
- case 92: return "integer overflow due to too many pixels";
- case 93: return "zero width or height is invalid";
- case 94: return "header chunk must have a size of 13 bytes";
- case 95: return "integer overflow with combined idat chunk size";
- case 96: return "invalid gAMA chunk size";
- case 97: return "invalid cHRM chunk size";
- case 98: return "invalid sRGB chunk size";
- case 99: return "invalid sRGB rendering intent";
- case 100: return "invalid ICC profile color type, the PNG specification only allows RGB or GRAY";
- case 101: return "PNG specification does not allow RGB ICC profile on gray color types and vice versa";
- case 102: return "not allowed to set grayscale ICC profile with colored pixels by PNG specification";
- case 103: return "invalid palette index in bKGD chunk. Maybe it came before PLTE chunk?";
- case 104: return "invalid bKGD color while encoding (e.g. palette index out of range)";
- case 105: return "integer overflow of bitsize";
- case 106: return "PNG file must have PLTE chunk if color type is palette";
- case 107: return "color convert from palette mode requested without setting the palette data in it";
- case 108: return "tried to add more than 256 values to a palette";
- /*this limit can be configured in LodePNGDecompressSettings*/
- case 109: return "tried to decompress zlib or deflate data larger than desired max_output_size";
- case 110: return "custom zlib or inflate decompression failed";
- case 111: return "custom zlib or deflate compression failed";
- /*max text size limit can be configured in LodePNGDecoderSettings. This error prevents
- unreasonable memory consumption when decoding due to impossibly large text sizes.*/
- case 112: return "compressed text unreasonably large";
- /*max ICC size limit can be configured in LodePNGDecoderSettings. This error prevents
- unreasonable memory consumption when decoding due to impossibly large ICC profile*/
- case 113: return "ICC profile unreasonably large";
- case 114: return "sBIT chunk has wrong size for the color type of the image";
- case 115: return "sBIT value out of range";
- }
- return "unknown error code";
-}
-#endif /*LODEPNG_COMPILE_ERROR_TEXT*/
-
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* // C++ Wrapper // */
-/* ////////////////////////////////////////////////////////////////////////// */
-/* ////////////////////////////////////////////////////////////////////////// */
-
-#ifdef LODEPNG_COMPILE_CPP
-namespace lodepng {
-
-#ifdef LODEPNG_COMPILE_DISK
-unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename) {
- long size = lodepng_filesize(filename.c_str());
- if(size < 0) return 78;
- buffer.resize((size_t)size);
- return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str());
-}
-
-/*write given buffer to the file, overwriting the file, it doesn't append to it.*/
-unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename) {
- return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str());
-}
-#endif /* LODEPNG_COMPILE_DISK */
-
-#ifdef LODEPNG_COMPILE_ZLIB
-#ifdef LODEPNG_COMPILE_DECODER
-unsigned decompress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
- const LodePNGDecompressSettings& settings) {
- unsigned char* buffer = 0;
- size_t buffersize = 0;
- unsigned error = zlib_decompress(&buffer, &buffersize, 0, in, insize, &settings);
- if(buffer) {
- out.insert(out.end(), buffer, &buffer[buffersize]);
- lodepng_free(buffer);
- }
- return error;
-}
-
-unsigned decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
- const LodePNGDecompressSettings& settings) {
- return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings);
-}
-#endif /* LODEPNG_COMPILE_DECODER */
-
-#ifdef LODEPNG_COMPILE_ENCODER
-unsigned compress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
- const LodePNGCompressSettings& settings) {
- unsigned char* buffer = 0;
- size_t buffersize = 0;
- unsigned error = zlib_compress(&buffer, &buffersize, in, insize, &settings);
- if(buffer) {
- out.insert(out.end(), buffer, &buffer[buffersize]);
- lodepng_free(buffer);
- }
- return error;
-}
-
-unsigned compress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
- const LodePNGCompressSettings& settings) {
- return compress(out, in.empty() ? 0 : &in[0], in.size(), settings);
-}
-#endif /* LODEPNG_COMPILE_ENCODER */
-#endif /* LODEPNG_COMPILE_ZLIB */
-
-
-#ifdef LODEPNG_COMPILE_PNG
-
-State::State() {
- lodepng_state_init(this);
-}
-
-State::State(const State& other) {
- lodepng_state_init(this);
- lodepng_state_copy(this, &other);
-}
-
-State::~State() {
- lodepng_state_cleanup(this);
-}
-
-State& State::operator=(const State& other) {
- lodepng_state_copy(this, &other);
- return *this;
-}
-
-#ifdef LODEPNG_COMPILE_DECODER
-
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const unsigned char* in,
- size_t insize, LodePNGColorType colortype, unsigned bitdepth) {
- unsigned char* buffer = 0;
- unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth);
- if(buffer && !error) {
- State state;
- state.info_raw.colortype = colortype;
- state.info_raw.bitdepth = bitdepth;
- size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw);
- out.insert(out.end(), buffer, &buffer[buffersize]);
- }
- lodepng_free(buffer);
- return error;
-}
-
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
- const std::vector<unsigned char>& in, LodePNGColorType colortype, unsigned bitdepth) {
- return decode(out, w, h, in.empty() ? 0 : &in[0], (unsigned)in.size(), colortype, bitdepth);
-}
-
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
- State& state,
- const unsigned char* in, size_t insize) {
- unsigned char* buffer = NULL;
- unsigned error = lodepng_decode(&buffer, &w, &h, &state, in, insize);
- if(buffer && !error) {
- size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw);
- out.insert(out.end(), buffer, &buffer[buffersize]);
- }
- lodepng_free(buffer);
- return error;
-}
-
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
- State& state,
- const std::vector<unsigned char>& in) {
- return decode(out, w, h, state, in.empty() ? 0 : &in[0], in.size());
-}
-
-#ifdef LODEPNG_COMPILE_DISK
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const std::string& filename,
- LodePNGColorType colortype, unsigned bitdepth) {
- std::vector<unsigned char> buffer;
- /* safe output values in case error happens */
- w = h = 0;
- unsigned error = load_file(buffer, filename);
- if(error) return error;
- return decode(out, w, h, buffer, colortype, bitdepth);
-}
-#endif /* LODEPNG_COMPILE_DECODER */
-#endif /* LODEPNG_COMPILE_DISK */
-
-#ifdef LODEPNG_COMPILE_ENCODER
-unsigned encode(std::vector<unsigned char>& out, const unsigned char* in, unsigned w, unsigned h,
- LodePNGColorType colortype, unsigned bitdepth) {
- unsigned char* buffer;
- size_t buffersize;
- unsigned error = lodepng_encode_memory(&buffer, &buffersize, in, w, h, colortype, bitdepth);
- if(buffer) {
- out.insert(out.end(), buffer, &buffer[buffersize]);
- lodepng_free(buffer);
- }
- return error;
-}
-
-unsigned encode(std::vector<unsigned char>& out,
- const std::vector<unsigned char>& in, unsigned w, unsigned h,
- LodePNGColorType colortype, unsigned bitdepth) {
- if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84;
- return encode(out, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth);
-}
-
-unsigned encode(std::vector<unsigned char>& out,
- const unsigned char* in, unsigned w, unsigned h,
- State& state) {
- unsigned char* buffer;
- size_t buffersize;
- unsigned error = lodepng_encode(&buffer, &buffersize, in, w, h, &state);
- if(buffer) {
- out.insert(out.end(), buffer, &buffer[buffersize]);
- lodepng_free(buffer);
- }
- return error;
-}
-
-unsigned encode(std::vector<unsigned char>& out,
- const std::vector<unsigned char>& in, unsigned w, unsigned h,
- State& state) {
- if(lodepng_get_raw_size(w, h, &state.info_raw) > in.size()) return 84;
- return encode(out, in.empty() ? 0 : &in[0], w, h, state);
-}
-
-#ifdef LODEPNG_COMPILE_DISK
-unsigned encode(const std::string& filename,
- const unsigned char* in, unsigned w, unsigned h,
- LodePNGColorType colortype, unsigned bitdepth) {
- std::vector<unsigned char> buffer;
- unsigned error = encode(buffer, in, w, h, colortype, bitdepth);
- if(!error) error = save_file(buffer, filename);
- return error;
-}
-
-unsigned encode(const std::string& filename,
- const std::vector<unsigned char>& in, unsigned w, unsigned h,
- LodePNGColorType colortype, unsigned bitdepth) {
- if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84;
- return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth);
-}
-#endif /* LODEPNG_COMPILE_DISK */
-#endif /* LODEPNG_COMPILE_ENCODER */
-#endif /* LODEPNG_COMPILE_PNG */
-} /* namespace lodepng */
-#endif /*LODEPNG_COMPILE_CPP*/
diff --git a/lodepng.h b/lodepng.h
deleted file mode 100644
index 81d49853b652..000000000000
--- a/lodepng.h
+++ /dev/null
@@ -1,2089 +0,0 @@
-/*
-LodePNG version 20230410
-
-Copyright (c) 2005-2023 Lode Vandevenne
-
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source
- distribution.
-*/
-
-#ifndef LODEPNG_H
-#define LODEPNG_H
-
-#include <string.h> /*for size_t*/
-
-extern const char* LODEPNG_VERSION_STRING;
-
-/*
-The following #defines are used to create code sections. They can be disabled
-to disable code sections, which can give faster compile time and smaller binary.
-The "NO_COMPILE" defines are designed to be used to pass as defines to the
-compiler command to disable them without modifying this header, e.g.
--DLODEPNG_NO_COMPILE_ZLIB for gcc or clang.
-*/
-/*deflate & zlib. If disabled, you must specify alternative zlib functions in
-the custom_zlib field of the compress and decompress settings*/
-#ifndef LODEPNG_NO_COMPILE_ZLIB
-/*pass -DLODEPNG_NO_COMPILE_ZLIB to the compiler to disable this, or comment out LODEPNG_COMPILE_ZLIB below*/
-#define LODEPNG_COMPILE_ZLIB
-#endif
-
-/*png encoder and png decoder*/
-#ifndef LODEPNG_NO_COMPILE_PNG
-/*pass -DLODEPNG_NO_COMPILE_PNG to the compiler to disable this, or comment out LODEPNG_COMPILE_PNG below*/
-#define LODEPNG_COMPILE_PNG
-#endif
-
-/*deflate&zlib decoder and png decoder*/
-#ifndef LODEPNG_NO_COMPILE_DECODER
-/*pass -DLODEPNG_NO_COMPILE_DECODER to the compiler to disable this, or comment out LODEPNG_COMPILE_DECODER below*/
-#define LODEPNG_COMPILE_DECODER
-#endif
-
-/*deflate&zlib encoder and png encoder*/
-#ifndef LODEPNG_NO_COMPILE_ENCODER
-/*pass -DLODEPNG_NO_COMPILE_ENCODER to the compiler to disable this, or comment out LODEPNG_COMPILE_ENCODER below*/
-#define LODEPNG_COMPILE_ENCODER
-#endif
-
-/*the optional built in harddisk file loading and saving functions*/
-#ifndef LODEPNG_NO_COMPILE_DISK
-/*pass -DLODEPNG_NO_COMPILE_DISK to the compiler to disable this, or comment out LODEPNG_COMPILE_DISK below*/
-#define LODEPNG_COMPILE_DISK
-#endif
-
-/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/
-#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
-/*pass -DLODEPNG_NO_COMPILE_ANCILLARY_CHUNKS to the compiler to disable this,
-or comment out LODEPNG_COMPILE_ANCILLARY_CHUNKS below*/
-#define LODEPNG_COMPILE_ANCILLARY_CHUNKS
-#endif
-
-/*ability to convert error numerical codes to English text string*/
-#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT
-/*pass -DLODEPNG_NO_COMPILE_ERROR_TEXT to the compiler to disable this,
-or comment out LODEPNG_COMPILE_ERROR_TEXT below*/
-#define LODEPNG_COMPILE_ERROR_TEXT
-#endif
-
-/*Compile the default allocators (C's free, malloc and realloc). If you disable this,
-you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your
-source files with custom allocators.*/
-#ifndef LODEPNG_NO_COMPILE_ALLOCATORS
-/*pass -DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler to disable the built-in ones,
-or comment out LODEPNG_COMPILE_ALLOCATORS below*/
-#define LODEPNG_COMPILE_ALLOCATORS
-#endif
-
-/*Disable built-in CRC function, in that case a custom implementation of
-lodepng_crc32 must be defined externally so that it can be linked in.
-The default built-in CRC code comes with 8KB of lookup tables, so for memory constrained environment you may want it
-disabled and provide a much smaller implementation externally as said above. You can find such an example implementation
-in a comment in the lodepng.c(pp) file in the 'else' case of the searchable LODEPNG_COMPILE_CRC section.*/
-#ifndef LODEPNG_NO_COMPILE_CRC
-/*pass -DLODEPNG_NO_COMPILE_CRC to the compiler to disable the built-in one,
-or comment out LODEPNG_COMPILE_CRC below*/
-#define LODEPNG_COMPILE_CRC
-#endif
-
-/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/
-#ifdef __cplusplus
-#ifndef LODEPNG_NO_COMPILE_CPP
-/*pass -DLODEPNG_NO_COMPILE_CPP to the compiler to disable C++ (not needed if a C-only compiler),
-or comment out LODEPNG_COMPILE_CPP below*/
-#define LODEPNG_COMPILE_CPP
-#endif
-#endif
-
-#ifdef LODEPNG_COMPILE_CPP
-#include <vector>
-#include <string>
-#endif /*LODEPNG_COMPILE_CPP*/
-
-#ifdef LODEPNG_COMPILE_PNG
-/*The PNG color types (also used for raw image).*/
-typedef enum LodePNGColorType {
- LCT_GREY = 0, /*grayscale: 1,2,4,8,16 bit*/
- LCT_RGB = 2, /*RGB: 8,16 bit*/
- LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/
- LCT_GREY_ALPHA = 4, /*grayscale with alpha: 8,16 bit*/
- LCT_RGBA = 6, /*RGB with alpha: 8,16 bit*/
- /*LCT_MAX_OCTET_VALUE lets the compiler allow this enum to represent any invalid
- byte value from 0 to 255 that could be present in an invalid PNG file header. Do
- not use, compare with or set the name LCT_MAX_OCTET_VALUE, instead either use
- the valid color type names above, or numeric values like 1 or 7 when checking for
- particular disallowed color type byte values, or cast to integer to print it.*/
- LCT_MAX_OCTET_VALUE = 255
-} LodePNGColorType;
-
-#ifdef LODEPNG_COMPILE_DECODER
-/*
-Converts PNG data in memory to raw pixel data.
-out: Output parameter. Pointer to buffer that will contain the raw pixel data.
- After decoding, its size is w * h * (bytes per pixel) bytes larger than
- initially. Bytes per pixel depends on colortype and bitdepth.
- Must be freed after usage with free(*out).
- Note: for 16-bit per channel colors, uses big endian format like PNG does.
-w: Output parameter. Pointer to width of pixel data.
-h: Output parameter. Pointer to height of pixel data.
-in: Memory buffer with the PNG file.
-insize: size of the in buffer.
-colortype: the desired color type for the raw output image. See explanation on PNG color types.
-bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types.
-Return value: LodePNG error code (0 means no error).
-*/
-unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h,
- const unsigned char* in, size_t insize,
- LodePNGColorType colortype, unsigned bitdepth);
-
-/*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/
-unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h,
- const unsigned char* in, size_t insize);
-
-/*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/
-unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h,
- const unsigned char* in, size_t insize);
-
-#ifdef LODEPNG_COMPILE_DISK
-/*
-Load PNG from disk, from file with given name.
-Same as the other decode functions, but instead takes a filename as input.
-
-NOTE: Wide-character filenames are not supported, you can use an external method
-to handle such files and decode in-memory.*/
-unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h,
- const char* filename,
- LodePNGColorType colortype, unsigned bitdepth);
-
-/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.
-
-NOTE: Wide-character filenames are not supported, you can use an external method
-to handle such files and decode in-memory.*/
-unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h,
- const char* filename);
-
-/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.
-
-NOTE: Wide-character filenames are not supported, you can use an external method
-to handle such files and decode in-memory.*/
-unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h,
- const char* filename);
-#endif /*LODEPNG_COMPILE_DISK*/
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/*
-Converts raw pixel data into a PNG image in memory. The colortype and bitdepth
- of the output PNG image cannot be chosen, they are automatically determined
- by the colortype, bitdepth and content of the input pixel data.
- Note: for 16-bit per channel colors, needs big endian format like PNG does.
-out: Output parameter. Pointer to buffer that will contain the PNG image data.
- Must be freed after usage with free(*out).
-outsize: Output parameter. Pointer to the size in bytes of the out buffer.
-image: The raw pixel data to encode. The size of this buffer should be
- w * h * (bytes per pixel), bytes per pixel depends on colortype and bitdepth.
-w: width of the raw pixel data in pixels.
-h: height of the raw pixel data in pixels.
-colortype: the color type of the raw input image. See explanation on PNG color types.
-bitdepth: the bit depth of the raw input image. See explanation on PNG color types.
-Return value: LodePNG error code (0 means no error).
-*/
-unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize,
- const unsigned char* image, unsigned w, unsigned h,
- LodePNGColorType colortype, unsigned bitdepth);
-
-/*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/
-unsigned lodepng_encode32(unsigned char** out, size_t* outsize,
- const unsigned char* image, unsigned w, unsigned h);
-
-/*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/
-unsigned lodepng_encode24(unsigned char** out, size_t* outsize,
- const unsigned char* image, unsigned w, unsigned h);
-
-#ifdef LODEPNG_COMPILE_DISK
-/*
-Converts raw pixel data into a PNG file on disk.
-Same as the other encode functions, but instead takes a filename as output.
-
-NOTE: This overwrites existing files without warning!
-
-NOTE: Wide-character filenames are not supported, you can use an external method
-to handle such files and encode in-memory.*/
-unsigned lodepng_encode_file(const char* filename,
- const unsigned char* image, unsigned w, unsigned h,
- LodePNGColorType colortype, unsigned bitdepth);
-
-/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.
-
-NOTE: Wide-character filenames are not supported, you can use an external method
-to handle such files and encode in-memory.*/
-unsigned lodepng_encode32_file(const char* filename,
- const unsigned char* image, unsigned w, unsigned h);
-
-/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.
-
-NOTE: Wide-character filenames are not supported, you can use an external method
-to handle such files and encode in-memory.*/
-unsigned lodepng_encode24_file(const char* filename,
- const unsigned char* image, unsigned w, unsigned h);
-#endif /*LODEPNG_COMPILE_DISK*/
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-
-#ifdef LODEPNG_COMPILE_CPP
-namespace lodepng {
-#ifdef LODEPNG_COMPILE_DECODER
-/*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype
-is the format to output the pixels to. Default is RGBA 8-bit per channel.*/
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
- const unsigned char* in, size_t insize,
- LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
- const std::vector<unsigned char>& in,
- LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-#ifdef LODEPNG_COMPILE_DISK
-/*
-Converts PNG file from disk to raw pixel data in memory.
-Same as the other decode functions, but instead takes a filename as input.
-
-NOTE: Wide-character filenames are not supported, you can use an external method
-to handle such files and decode in-memory.
-*/
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
- const std::string& filename,
- LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-#endif /* LODEPNG_COMPILE_DISK */
-#endif /* LODEPNG_COMPILE_DECODER */
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/*Same as lodepng_encode_memory, but encodes to an std::vector. colortype
-is that of the raw input data. The output PNG color type will be auto chosen.*/
-unsigned encode(std::vector<unsigned char>& out,
- const unsigned char* in, unsigned w, unsigned h,
- LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-unsigned encode(std::vector<unsigned char>& out,
- const std::vector<unsigned char>& in, unsigned w, unsigned h,
- LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-#ifdef LODEPNG_COMPILE_DISK
-/*
-Converts 32-bit RGBA raw pixel data into a PNG file on disk.
-Same as the other encode functions, but instead takes a filename as output.
-
-NOTE: This overwrites existing files without warning!
-
-NOTE: Wide-character filenames are not supported, you can use an external method
-to handle such files and decode in-memory.
-*/
-unsigned encode(const std::string& filename,
- const unsigned char* in, unsigned w, unsigned h,
- LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-unsigned encode(const std::string& filename,
- const std::vector<unsigned char>& in, unsigned w, unsigned h,
- LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8);
-#endif /* LODEPNG_COMPILE_DISK */
-#endif /* LODEPNG_COMPILE_ENCODER */
-} /* namespace lodepng */
-#endif /*LODEPNG_COMPILE_CPP*/
-#endif /*LODEPNG_COMPILE_PNG*/
-
-#ifdef LODEPNG_COMPILE_ERROR_TEXT
-/*Returns an English description of the numerical error code.*/
-const char* lodepng_error_text(unsigned code);
-#endif /*LODEPNG_COMPILE_ERROR_TEXT*/
-
-#ifdef LODEPNG_COMPILE_DECODER
-/*Settings for zlib decompression*/
-typedef struct LodePNGDecompressSettings LodePNGDecompressSettings;
-struct LodePNGDecompressSettings {
- /* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */
- unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/
- unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/
-
- /*Maximum decompressed size, beyond this the decoder may (and is encouraged to) stop decoding,
- return an error, output a data size > max_output_size and all the data up to that point. This is
- not hard limit nor a guarantee, but can prevent excessive memory usage. This setting is
- ignored by the PNG decoder, but is used by the deflate/zlib decoder and can be used by custom ones.
- Set to 0 to impose no limit (the default).*/
- size_t max_output_size;
-
- /*use custom zlib decoder instead of built in one (default: null).
- Should return 0 if success, any non-0 if error (numeric value not exposed).*/
- unsigned (*custom_zlib)(unsigned char**, size_t*,
- const unsigned char*, size_t,
- const LodePNGDecompressSettings*);
- /*use custom deflate decoder instead of built in one (default: null)
- if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate).
- Should return 0 if success, any non-0 if error (numeric value not exposed).*/
- unsigned (*custom_inflate)(unsigned char**, size_t*,
- const unsigned char*, size_t,
- const LodePNGDecompressSettings*);
-
- const void* custom_context; /*optional custom settings for custom functions*/
-};
-
-extern const LodePNGDecompressSettings lodepng_default_decompress_settings;
-void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings);
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/*
-Settings for zlib compression. Tweaking these settings tweaks the balance
-between speed and compression ratio.
-*/
-typedef struct LodePNGCompressSettings LodePNGCompressSettings;
-struct LodePNGCompressSettings /*deflate = compress*/ {
- /*LZ77 related settings*/
- unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/
- unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/
- unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/
- unsigned minmatch; /*minimum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/
- unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/
- unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/
-
- /*use custom zlib encoder instead of built in one (default: null)*/
- unsigned (*custom_zlib)(unsigned char**, size_t*,
- const unsigned char*, size_t,
- const LodePNGCompressSettings*);
- /*use custom deflate encoder instead of built in one (default: null)
- if custom_zlib is used, custom_deflate is ignored since only the built in
- zlib function will call custom_deflate*/
- unsigned (*custom_deflate)(unsigned char**, size_t*,
- const unsigned char*, size_t,
- const LodePNGCompressSettings*);
-
- const void* custom_context; /*optional custom settings for custom functions*/
-};
-
-extern const LodePNGCompressSettings lodepng_default_compress_settings;
-void lodepng_compress_settings_init(LodePNGCompressSettings* settings);
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-#ifdef LODEPNG_COMPILE_PNG
-/*
-Color mode of an image. Contains all information required to decode the pixel
-bits to RGBA colors. This information is the same as used in the PNG file
-format, and is used both for PNG and raw image data in LodePNG.
-*/
-typedef struct LodePNGColorMode {
- /*header (IHDR)*/
- LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/
- unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/
-
- /*
- palette (PLTE and tRNS)
-
- Dynamically allocated with the colors of the palette, including alpha.
- This field may not be allocated directly, use lodepng_color_mode_init first,
- then lodepng_palette_add per color to correctly initialize it (to ensure size
- of exactly 1024 bytes).
-
- The alpha channels must be set as well, set them to 255 for opaque images.
-
- When decoding, with the default settings you can ignore this palette, since
- LodePNG already fills the palette colors in the pixels of the raw RGBA output,
- but when decoding to the original PNG color mode it is needed to reconstruct
- the colors.
-
- The palette is only supported for color type 3.
- */
- unsigned char* palette; /*palette in RGBARGBA... order. Must be either 0, or when allocated must have 1024 bytes*/
- size_t palettesize; /*palette size in number of colors (amount of used bytes is 4 * palettesize)*/
-
- /*
- transparent color key (tRNS)
-
- This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit.
- For grayscale PNGs, r, g and b will all 3 be set to the same.
-
- When decoding, by default you can ignore this information, since LodePNG sets
- pixels with this key to transparent already in the raw RGBA output.
-
- The color key is only supported for color types 0 and 2.
- */
- unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/
- unsigned key_r; /*red/grayscale component of color key*/
- unsigned key_g; /*green component of color key*/
- unsigned key_b; /*blue component of color key*/
-} LodePNGColorMode;
-
-/*init, cleanup and copy functions to use with this struct*/
-void lodepng_color_mode_init(LodePNGColorMode* info);
-void lodepng_color_mode_cleanup(LodePNGColorMode* info);
-/*return value is error code (0 means no error)*/
-unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source);
-/* Makes a temporary LodePNGColorMode that does not need cleanup (no palette) */
-LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth);
-
-void lodepng_palette_clear(LodePNGColorMode* info);
-/*add 1 color to the palette*/
-unsigned lodepng_palette_add(LodePNGColorMode* info,
- unsigned char r, unsigned char g, unsigned char b, unsigned char a);
-
-/*get the total amount of bits per pixel, based on colortype and bitdepth in the struct*/
-unsigned lodepng_get_bpp(const LodePNGColorMode* info);
-/*get the amount of color channels used, based on colortype in the struct.
-If a palette is used, it counts as 1 channel.*/
-unsigned lodepng_get_channels(const LodePNGColorMode* info);
-/*is it a grayscale type? (only colortype 0 or 4)*/
-unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info);
-/*has it got an alpha channel? (only colortype 2 or 6)*/
-unsigned lodepng_is_alpha_type(const LodePNGColorMode* info);
-/*has it got a palette? (only colortype 3)*/
-unsigned lodepng_is_palette_type(const LodePNGColorMode* info);
-/*only returns true if there is a palette and there is a value in the palette with alpha < 255.
-Loops through the palette to check this.*/
-unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info);
-/*
-Check if the given color info indicates the possibility of having non-opaque pixels in the PNG image.
-Returns true if the image can have translucent or invisible pixels (it still be opaque if it doesn't use such pixels).
-Returns false if the image can only have opaque pixels.
-In detail, it returns true only if it's a color type with alpha, or has a palette with non-opaque values,
-or if "key_defined" is true.
-*/
-unsigned lodepng_can_have_alpha(const LodePNGColorMode* info);
-/*Returns the byte size of a raw image buffer with given width, height and color mode*/
-size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color);
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-/*The information of a Time chunk in PNG.*/
-typedef struct LodePNGTime {
- unsigned year; /*2 bytes used (0-65535)*/
- unsigned month; /*1-12*/
- unsigned day; /*1-31*/
- unsigned hour; /*0-23*/
- unsigned minute; /*0-59*/
- unsigned second; /*0-60 (to allow for leap seconds)*/
-} LodePNGTime;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-/*Information about the PNG image, except pixels, width and height.*/
-typedef struct LodePNGInfo {
- /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/
- unsigned compression_method;/*compression method of the original file. Always 0.*/
- unsigned filter_method; /*filter method of the original file*/
- unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/
- LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- /*
- Suggested background color chunk (bKGD)
-
- This uses the same color mode and bit depth as the PNG (except no alpha channel),
- with values truncated to the bit depth in the unsigned integer.
-
- For grayscale and palette PNGs, the value is stored in background_r. The values
- in background_g and background_b are then unused. The decoder will set them
- equal to background_r, the encoder ignores them in this case.
-
- When decoding, you may get these in a different color mode than the one you requested
- for the raw pixels: the colortype and bitdepth defined by info_png.color, that is the
- ones defined in the header of the PNG image, are used.
-
- When encoding with auto_convert, you must use the color model defined in info_png.color for
- these values. The encoder normally ignores info_png.color when auto_convert is on, but will
- use it to interpret these values (and convert copies of them to its chosen color model).
-
- When encoding, avoid setting this to an expensive color, such as a non-gray value
- when the image is gray, or the compression will be worse since it will be forced to
- write the PNG with a more expensive color mode (when auto_convert is on).
-
- The decoder does not use this background color to edit the color of pixels. This is a
- completely optional metadata feature.
- */
- unsigned background_defined; /*is a suggested background color given?*/
- unsigned background_r; /*red/gray/palette component of suggested background color*/
- unsigned background_g; /*green component of suggested background color*/
- unsigned background_b; /*blue component of suggested background color*/
-
- /*
- Non-international text chunks (tEXt and zTXt)
-
- The char** arrays each contain num strings. The actual messages are in
- text_strings, while text_keys are keywords that give a short description what
- the actual text represents, e.g. Title, Author, Description, or anything else.
-
- All the string fields below including strings, keys, names and language tags are null terminated.
- The PNG specification uses null characters for the keys, names and tags, and forbids null
- characters to appear in the main text which is why we can use null termination everywhere here.
-
- A keyword is minimum 1 character and maximum 79 characters long (plus the
- additional null terminator). It's discouraged to use a single line length
- longer than 79 characters for texts.
-
- Don't allocate these text buffers yourself. Use the init/cleanup functions
- correctly and use lodepng_add_text and lodepng_clear_text.
-
- Standard text chunk keywords and strings are encoded using Latin-1.
- */
- size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/
- char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/
- char** text_strings; /*the actual text*/
-
- /*
- International text chunks (iTXt)
- Similar to the non-international text chunks, but with additional strings
- "langtags" and "transkeys", and the following text encodings are used:
- keys: Latin-1, langtags: ASCII, transkeys and strings: UTF-8.
- keys must be 1-79 characters (plus the additional null terminator), the other
- strings are any length.
- */
- size_t itext_num; /*the amount of international texts in this PNG*/
- char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/
- char** itext_langtags; /*language tag for this text's language, ISO/IEC 646 string, e.g. ISO 639 language tag*/
- char** itext_transkeys; /*keyword translated to the international language - UTF-8 string*/
- char** itext_strings; /*the actual international text - UTF-8 string*/
-
- /*time chunk (tIME)*/
- unsigned time_defined; /*set to 1 to make the encoder generate a tIME chunk*/
- LodePNGTime time;
-
- /*phys chunk (pHYs)*/
- unsigned phys_defined; /*if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one*/
- unsigned phys_x; /*pixels per unit in x direction*/
- unsigned phys_y; /*pixels per unit in y direction*/
- unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/
-
- /*
- Color profile related chunks: gAMA, cHRM, sRGB, iCPP, sBIT
-
- LodePNG does not apply any color conversions on pixels in the encoder or decoder and does not interpret these color
- profile values. It merely passes on the information. If you wish to use color profiles and convert colors, please
- use these values with a color management library.
-
- See the PNG, ICC and sRGB specifications for more information about the meaning of these values.
- */
-
- /* gAMA chunk: optional, overridden by sRGB or iCCP if those are present. */
- unsigned gama_defined; /* Whether a gAMA chunk is present (0 = not present, 1 = present). */
- unsigned gama_gamma; /* Gamma exponent times 100000 */
-
- /* cHRM chunk: optional, overridden by sRGB or iCCP if those are present. */
- unsigned chrm_defined; /* Whether a cHRM chunk is present (0 = not present, 1 = present). */
- unsigned chrm_white_x; /* White Point x times 100000 */
- unsigned chrm_white_y; /* White Point y times 100000 */
- unsigned chrm_red_x; /* Red x times 100000 */
- unsigned chrm_red_y; /* Red y times 100000 */
- unsigned chrm_green_x; /* Green x times 100000 */
- unsigned chrm_green_y; /* Green y times 100000 */
- unsigned chrm_blue_x; /* Blue x times 100000 */
- unsigned chrm_blue_y; /* Blue y times 100000 */
-
- /*
- sRGB chunk: optional. May not appear at the same time as iCCP.
- If gAMA is also present gAMA must contain value 45455.
- If cHRM is also present cHRM must contain respectively 31270,32900,64000,33000,30000,60000,15000,6000.
- */
- unsigned srgb_defined; /* Whether an sRGB chunk is present (0 = not present, 1 = present). */
- unsigned srgb_intent; /* Rendering intent: 0=perceptual, 1=rel. colorimetric, 2=saturation, 3=abs. colorimetric */
-
- /*
- iCCP chunk: optional. May not appear at the same time as sRGB.
-
- LodePNG does not parse or use the ICC profile (except its color space header field for an edge case), a
- separate library to handle the ICC data (not included in LodePNG) format is needed to use it for color
- management and conversions.
-
- For encoding, if iCCP is present, gAMA and cHRM are recommended to be added as well with values that match the ICC
- profile as closely as possible, if you wish to do this you should provide the correct values for gAMA and cHRM and
- enable their '_defined' flags since LodePNG will not automatically compute them from the ICC profile.
-
- For encoding, the ICC profile is required by the PNG specification to be an "RGB" profile for non-gray
- PNG color types and a "GRAY" profile for gray PNG color types. If you disable auto_convert, you must ensure
- the ICC profile type matches your requested color type, else the encoder gives an error. If auto_convert is
- enabled (the default), and the ICC profile is not a good match for the pixel data, this will result in an encoder
- error if the pixel data has non-gray pixels for a GRAY profile, or a silent less-optimal compression of the pixel
- data if the pixels could be encoded as grayscale but the ICC profile is RGB.
-
- To avoid this do not set an ICC profile in the image unless there is a good reason for it, and when doing so
- make sure you compute it carefully to avoid the above problems.
- */
- unsigned iccp_defined; /* Whether an iCCP chunk is present (0 = not present, 1 = present). */
- char* iccp_name; /* Null terminated string with profile name, 1-79 bytes */
- /*
- The ICC profile in iccp_profile_size bytes.
- Don't allocate this buffer yourself. Use the init/cleanup functions
- correctly and use lodepng_set_icc and lodepng_clear_icc.
- */
- unsigned char* iccp_profile;
- unsigned iccp_profile_size; /* The size of iccp_profile in bytes */
-
- /*
- sBIT chunk: significant bits. Optional metadata, only set this if needed.
-
- If defined, these values give the bit depth of the original data. Since PNG only stores 1, 2, 4, 8 or 16-bit
- per channel data, the significant bits value can be used to indicate the original encoded data has another
- sample depth, such as 10 or 12.
-
- Encoders using this value, when storing the pixel data, should use the most significant bits
- of the data to store the original bits, and use a good sample depth scaling method such as
- "left bit replication" to fill in the least significant bits, rather than fill zeroes.
-
- Decoders using this value, if able to work with data that's e.g. 10-bit or 12-bit, should right
- shift the data to go back to the original bit depth, but decoders are also allowed to ignore
- sbit and work e.g. with the 8-bit or 16-bit data from the PNG directly, since thanks
- to the encoder contract, the values encoded in PNG are in valid range for the PNG bit depth.
-
- For grayscale images, sbit_g and sbit_b are not used, and for images that don't use color
- type RGBA or grayscale+alpha, sbit_a is not used (it's not used even for palette images with
- translucent palette values, or images with color key). The values that are used must be
- greater than zero and smaller than or equal to the PNG bit depth.
-
- The color type from the header in the PNG image defines these used and unused fields: if
- decoding with a color mode conversion, such as always decoding to RGBA, this metadata still
- only uses the color type of the original PNG, and may e.g. lack the alpha channel info
- if the PNG was RGB. When encoding with auto_convert (as well as without), also always the
- color model defined in info_png.color determines this.
-
- NOTE: enabling sbit can hurt compression, because the encoder can then not always use
- auto_convert to choose a more optimal color mode for the data, because the PNG format has
- strict requirements for the allowed sbit values in combination with color modes.
- For example, setting these fields to 10-bit will force the encoder to keep using a 16-bit per channel
- color mode, even if the pixel data would in fact fit in a more efficient 8-bit mode.
- */
- unsigned sbit_defined; /*is significant bits given? if not, the values below are unused*/
- unsigned sbit_r; /*red or gray component of significant bits*/
- unsigned sbit_g; /*green component of significant bits*/
- unsigned sbit_b; /*blue component of significant bits*/
- unsigned sbit_a; /*alpha component of significant bits*/
-
- /* End of color profile related chunks */
-
-
- /*
- unknown chunks: chunks not known by LodePNG, passed on byte for byte.
-
- There are 3 buffers, one for each position in the PNG where unknown chunks can appear.
- Each buffer contains all unknown chunks for that position consecutively.
- The 3 positions are:
- 0: between IHDR and PLTE, 1: between PLTE and IDAT, 2: between IDAT and IEND.
-
- For encoding, do not store critical chunks or known chunks that are enabled with a "_defined" flag
- above in here, since the encoder will blindly follow this and could then encode an invalid PNG file
- (such as one with two IHDR chunks or the disallowed combination of sRGB with iCCP). But do use
- this if you wish to store an ancillary chunk that is not supported by LodePNG (such as sPLT or hIST),
- or any non-standard PNG chunk.
-
- Do not allocate or traverse this data yourself. Use the chunk traversing functions declared
- later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct.
- */
- unsigned char* unknown_chunks_data[3];
- size_t unknown_chunks_size[3]; /*size in bytes of the unknown chunks, given for protection*/
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-} LodePNGInfo;
-
-/*init, cleanup and copy functions to use with this struct*/
-void lodepng_info_init(LodePNGInfo* info);
-void lodepng_info_cleanup(LodePNGInfo* info);
-/*return value is error code (0 means no error)*/
-unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source);
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
-unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/
-void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/
-
-unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag,
- const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/
-void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/
-
-/*replaces if exists*/
-unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size);
-void lodepng_clear_icc(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-
-/*
-Converts raw buffer from one color type to another color type, based on
-LodePNGColorMode structs to describe the input and output color type.
-See the reference manual at the end of this header file to see which color conversions are supported.
-return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported)
-The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel
-of the output color type (lodepng_get_bpp).
-For < 8 bpp images, there should not be padding bits at the end of scanlines.
-For 16-bit per channel colors, uses big endian format like PNG does.
-Return value is LodePNG error code
-*/
-unsigned lodepng_convert(unsigned char* out, const unsigned char* in,
- const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in,
- unsigned w, unsigned h);
-
-#ifdef LODEPNG_COMPILE_DECODER
-/*
-Settings for the decoder. This contains settings for the PNG and the Zlib
-decoder, but not the Info settings from the Info structs.
-*/
-typedef struct LodePNGDecoderSettings {
- LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/
-
- /* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */
- unsigned ignore_crc; /*ignore CRC checksums*/
- unsigned ignore_critical; /*ignore unknown critical chunks*/
- unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/
- /* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable
- errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some
- strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters
- in string keys, etc... */
-
- unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/
-
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/
-
- /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/
- unsigned remember_unknown_chunks;
-
- /* maximum size for decompressed text chunks. If a text chunk's text is larger than this, an error is returned,
- unless reading text chunks is disabled or this limit is set higher or disabled. Set to 0 to allow any size.
- By default it is a value that prevents unreasonably large strings from hogging memory. */
- size_t max_text_size;
-
- /* maximum size for compressed ICC chunks. If the ICC profile is larger than this, an error will be returned. Set to
- 0 to allow any size. By default this is a value that prevents ICC profiles that would be much larger than any
- legitimate profile could be to hog memory. */
- size_t max_icc_size;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-} LodePNGDecoderSettings;
-
-void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings);
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/
-typedef enum LodePNGFilterStrategy {
- /*every filter at zero*/
- LFS_ZERO = 0,
- /*every filter at 1, 2, 3 or 4 (paeth), unlike LFS_ZERO not a good choice, but for testing*/
- LFS_ONE = 1,
- LFS_TWO = 2,
- LFS_THREE = 3,
- LFS_FOUR = 4,
- /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/
- LFS_MINSUM,
- /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending
- on the image, this is better or worse than minsum.*/
- LFS_ENTROPY,
- /*
- Brute-force-search PNG filters by compressing each filter for each scanline.
- Experimental, very slow, and only rarely gives better compression than MINSUM.
- */
- LFS_BRUTE_FORCE,
- /*use predefined_filters buffer: you specify the filter type for each scanline*/
- LFS_PREDEFINED
-} LodePNGFilterStrategy;
-
-/*Gives characteristics about the integer RGBA colors of the image (count, alpha channel usage, bit depth, ...),
-which helps decide which color model to use for encoding.
-Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/
-typedef struct LodePNGColorStats {
- unsigned colored; /*not grayscale*/
- unsigned key; /*image is not opaque and color key is possible instead of full alpha*/
- unsigned short key_r; /*key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255*/
- unsigned short key_g;
- unsigned short key_b;
- unsigned alpha; /*image is not opaque and alpha channel or alpha palette required*/
- unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16 or allow_palette is disabled.*/
- unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order, only valid when numcolors is valid*/
- unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for grayscale only. 16 if 16-bit per channel required.*/
- size_t numpixels;
-
- /*user settings for computing/using the stats*/
- unsigned allow_palette; /*default 1. if 0, disallow choosing palette colortype in auto_choose_color, and don't count numcolors*/
- unsigned allow_greyscale; /*default 1. if 0, choose RGB or RGBA even if the image only has gray colors*/
-} LodePNGColorStats;
-
-void lodepng_color_stats_init(LodePNGColorStats* stats);
-
-/*Get a LodePNGColorStats of the image. The stats must already have been inited.
-Returns error code (e.g. alloc fail) or 0 if ok.*/
-unsigned lodepng_compute_color_stats(LodePNGColorStats* stats,
- const unsigned char* image, unsigned w, unsigned h,
- const LodePNGColorMode* mode_in);
-
-/*Settings for the encoder.*/
-typedef struct LodePNGEncoderSettings {
- LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/
-
- unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/
-
- /*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than
- 8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to
- completely follow the official PNG heuristic, filter_palette_zero must be true and
- filter_strategy must be LFS_MINSUM*/
- unsigned filter_palette_zero;
- /*Which filter strategy to use when not using zeroes due to filter_palette_zero.
- Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: LFS_MINSUM*/
- LodePNGFilterStrategy filter_strategy;
- /*used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with
- the same length as the amount of scanlines in the image, and each value must <= 5. You
- have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero
- must be set to 0 to ensure this is also used on palette or low bitdepth images.*/
- const unsigned char* predefined_filters;
-
- /*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette).
- If colortype is 3, PLTE is always created. If color type is explicitely set
- to a grayscale type (1 or 4), this is not done and is ignored. If enabling this,
- a palette must be present in the info_png.
- NOTE: enabling this may worsen compression if auto_convert is used to choose
- optimal color mode, because it cannot use grayscale color modes in this case*/
- unsigned force_palette;
-#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
- /*add LodePNG identifier and version as a text chunk, for debugging*/
- unsigned add_id;
- /*encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks*/
- unsigned text_compression;
-#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/
-} LodePNGEncoderSettings;
-
-void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings);
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-
-#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER)
-/*The settings, state and information for extended encoding and decoding.*/
-typedef struct LodePNGState {
-#ifdef LODEPNG_COMPILE_DECODER
- LodePNGDecoderSettings decoder; /*the decoding settings*/
-#endif /*LODEPNG_COMPILE_DECODER*/
-#ifdef LODEPNG_COMPILE_ENCODER
- LodePNGEncoderSettings encoder; /*the encoding settings*/
-#endif /*LODEPNG_COMPILE_ENCODER*/
- LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/
- LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/
- unsigned error;
-} LodePNGState;
-
-/*init, cleanup and copy functions to use with this struct*/
-void lodepng_state_init(LodePNGState* state);
-void lodepng_state_cleanup(LodePNGState* state);
-void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source);
-#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */
-
-#ifdef LODEPNG_COMPILE_DECODER
-/*
-Same as lodepng_decode_memory, but uses a LodePNGState to allow custom settings and
-getting much more information about the PNG image and color mode.
-*/
-unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h,
- LodePNGState* state,
- const unsigned char* in, size_t insize);
-
-/*
-Read the PNG header, but not the actual data. This returns only the information
-that is in the IHDR chunk of the PNG, such as width, height and color type. The
-information is placed in the info_png field of the LodePNGState.
-*/
-unsigned lodepng_inspect(unsigned* w, unsigned* h,
- LodePNGState* state,
- const unsigned char* in, size_t insize);
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-/*
-Reads one metadata chunk (other than IHDR, which is handled by lodepng_inspect)
-of the PNG file and outputs what it read in the state. Returns error code on failure.
-Use lodepng_inspect first with a new state, then e.g. lodepng_chunk_find_const
-to find the desired chunk type, and if non null use lodepng_inspect_chunk (with
-chunk_pointer - start_of_file as pos).
-Supports most metadata chunks from the PNG standard (gAMA, bKGD, tEXt, ...).
-Ignores unsupported, unknown, non-metadata or IHDR chunks (without error).
-Requirements: &in[pos] must point to start of a chunk, must use regular
-lodepng_inspect first since format of most other chunks depends on IHDR, and if
-there is a PLTE chunk, that one must be inspected before tRNS or bKGD.
-*/
-unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos,
- const unsigned char* in, size_t insize);
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/
-unsigned lodepng_encode(unsigned char** out, size_t* outsize,
- const unsigned char* image, unsigned w, unsigned h,
- LodePNGState* state);
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-/*
-The lodepng_chunk functions are normally not needed, except to traverse the
-unknown chunks stored in the LodePNGInfo struct, or add new ones to it.
-It also allows traversing the chunks of an encoded PNG file yourself.
-
-The chunk pointer always points to the beginning of the chunk itself, that is
-the first byte of the 4 length bytes.
-
-In the PNG file format, chunks have the following format:
--4 bytes length: length of the data of the chunk in bytes (chunk itself is 12 bytes longer)
--4 bytes chunk type (ASCII a-z,A-Z only, see below)
--length bytes of data (may be 0 bytes if length was 0)
--4 bytes of CRC, computed on chunk name + data
-
-The first chunk starts at the 8th byte of the PNG file, the entire rest of the file
-exists out of concatenated chunks with the above format.
-
-PNG standard chunk ASCII naming conventions:
--First byte: uppercase = critical, lowercase = ancillary
--Second byte: uppercase = public, lowercase = private
--Third byte: must be uppercase
--Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy
-*/
-
-/*
-Gets the length of the data of the chunk. Total chunk length has 12 bytes more.
-There must be at least 4 bytes to read from. If the result value is too large,
-it may be corrupt data.
-*/
-unsigned lodepng_chunk_length(const unsigned char* chunk);
-
-/*puts the 4-byte type in null terminated string*/
-void lodepng_chunk_type(char type[5], const unsigned char* chunk);
-
-/*check if the type is the given type*/
-unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type);
-
-/*0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard)*/
-unsigned char lodepng_chunk_ancillary(const unsigned char* chunk);
-
-/*0: public, 1: private (see PNG standard)*/
-unsigned char lodepng_chunk_private(const unsigned char* chunk);
-
-/*0: the chunk is unsafe to copy, 1: the chunk is safe to copy (see PNG standard)*/
-unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk);
-
-/*get pointer to the data of the chunk, where the input points to the header of the chunk*/
-unsigned char* lodepng_chunk_data(unsigned char* chunk);
-const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk);
-
-/*returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!)*/
-unsigned lodepng_chunk_check_crc(const unsigned char* chunk);
-
-/*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/
-void lodepng_chunk_generate_crc(unsigned char* chunk);
-
-/*
-Iterate to next chunks, allows iterating through all chunks of the PNG file.
-Input must be at the beginning of a chunk (result of a previous lodepng_chunk_next call,
-or the 8th byte of a PNG file which always has the first chunk), or alternatively may
-point to the first byte of the PNG file (which is not a chunk but the magic header, the
-function will then skip over it and return the first real chunk).
-Will output pointer to the start of the next chunk, or at or beyond end of the file if there
-is no more chunk after this or possibly if the chunk is corrupt.
-Start this process at the 8th byte of the PNG file.
-In a non-corrupt PNG file, the last chunk should have name "IEND".
-*/
-unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end);
-const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end);
-
-/*Finds the first chunk with the given type in the range [chunk, end), or returns NULL if not found.*/
-unsigned char* lodepng_chunk_find(unsigned char* chunk, unsigned char* end, const char type[5]);
-const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]);
-
-/*
-Appends chunk to the data in out. The given chunk should already have its chunk header.
-The out variable and outsize are updated to reflect the new reallocated buffer.
-Returns error code (0 if it went ok)
-*/
-unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk);
-
-/*
-Appends new chunk to out. The chunk to append is given by giving its length, type
-and data separately. The type is a 4-letter string.
-The out variable and outsize are updated to reflect the new reallocated buffer.
-Returne error code (0 if it went ok)
-*/
-unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length,
- const char* type, const unsigned char* data);
-
-
-/*Calculate CRC32 of buffer*/
-unsigned lodepng_crc32(const unsigned char* buf, size_t len);
-#endif /*LODEPNG_COMPILE_PNG*/
-
-
-#ifdef LODEPNG_COMPILE_ZLIB
-/*
-This zlib part can be used independently to zlib compress and decompress a
-buffer. It cannot be used to create gzip files however, and it only supports the
-part of zlib that is required for PNG, it does not support dictionaries.
-*/
-
-#ifdef LODEPNG_COMPILE_DECODER
-/*Inflate a buffer. Inflate is the decompression step of deflate. Out buffer must be freed after use.*/
-unsigned lodepng_inflate(unsigned char** out, size_t* outsize,
- const unsigned char* in, size_t insize,
- const LodePNGDecompressSettings* settings);
-
-/*
-Decompresses Zlib data. Reallocates the out buffer and appends the data. The
-data must be according to the zlib specification.
-Either, *out must be NULL and *outsize must be 0, or, *out must be a valid
-buffer and *outsize its size in bytes. out must be freed by user after usage.
-*/
-unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize,
- const unsigned char* in, size_t insize,
- const LodePNGDecompressSettings* settings);
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/*
-Compresses data with Zlib. Reallocates the out buffer and appends the data.
-Zlib adds a small header and trailer around the deflate data.
-The data is output in the format of the zlib specification.
-Either, *out must be NULL and *outsize must be 0, or, *out must be a valid
-buffer and *outsize its size in bytes. out must be freed by user after usage.
-*/
-unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize,
- const unsigned char* in, size_t insize,
- const LodePNGCompressSettings* settings);
-
-/*
-Find length-limited Huffman code for given frequencies. This function is in the
-public interface only for tests, it's used internally by lodepng_deflate.
-*/
-unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies,
- size_t numcodes, unsigned maxbitlen);
-
-/*Compress a buffer with deflate. See RFC 1951. Out buffer must be freed after use.*/
-unsigned lodepng_deflate(unsigned char** out, size_t* outsize,
- const unsigned char* in, size_t insize,
- const LodePNGCompressSettings* settings);
-
-#endif /*LODEPNG_COMPILE_ENCODER*/
-#endif /*LODEPNG_COMPILE_ZLIB*/
-
-#ifdef LODEPNG_COMPILE_DISK
-/*
-Load a file from disk into buffer. The function allocates the out buffer, and
-after usage you should free it.
-out: output parameter, contains pointer to loaded buffer.
-outsize: output parameter, size of the allocated out buffer
-filename: the path to the file to load
-return value: error code (0 means ok)
-
-NOTE: Wide-character filenames are not supported, you can use an external method
-to handle such files and decode in-memory.
-*/
-unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename);
-
-/*
-Save a file from buffer to disk. Warning, if it exists, this function overwrites
-the file without warning!
-buffer: the buffer to write
-buffersize: size of the buffer to write
-filename: the path to the file to save to
-return value: error code (0 means ok)
-
-NOTE: Wide-character filenames are not supported, you can use an external method
-to handle such files and encode in-memory
-*/
-unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename);
-#endif /*LODEPNG_COMPILE_DISK*/
-
-#ifdef LODEPNG_COMPILE_CPP
-/* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */
-namespace lodepng {
-#ifdef LODEPNG_COMPILE_PNG
-class State : public LodePNGState {
- public:
- State();
- State(const State& other);
- ~State();
- State& operator=(const State& other);
-};
-
-#ifdef LODEPNG_COMPILE_DECODER
-/* Same as other lodepng::decode, but using a State for more settings and information. */
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
- State& state,
- const unsigned char* in, size_t insize);
-unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h,
- State& state,
- const std::vector<unsigned char>& in);
-#endif /*LODEPNG_COMPILE_DECODER*/
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/* Same as other lodepng::encode, but using a State for more settings and information. */
-unsigned encode(std::vector<unsigned char>& out,
- const unsigned char* in, unsigned w, unsigned h,
- State& state);
-unsigned encode(std::vector<unsigned char>& out,
- const std::vector<unsigned char>& in, unsigned w, unsigned h,
- State& state);
-#endif /*LODEPNG_COMPILE_ENCODER*/
-
-#ifdef LODEPNG_COMPILE_DISK
-/*
-Load a file from disk into an std::vector.
-return value: error code (0 means ok)
-
-NOTE: Wide-character filenames are not supported, you can use an external method
-to handle such files and decode in-memory
-*/
-unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename);
-
-/*
-Save the binary data in an std::vector to a file on disk. The file is overwritten
-without warning.
-
-NOTE: Wide-character filenames are not supported, you can use an external method
-to handle such files and encode in-memory
-*/
-unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename);
-#endif /* LODEPNG_COMPILE_DISK */
-#endif /* LODEPNG_COMPILE_PNG */
-
-#ifdef LODEPNG_COMPILE_ZLIB
-#ifdef LODEPNG_COMPILE_DECODER
-/* Zlib-decompress an unsigned char buffer */
-unsigned decompress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
- const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings);
-
-/* Zlib-decompress an std::vector */
-unsigned decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
- const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings);
-#endif /* LODEPNG_COMPILE_DECODER */
-
-#ifdef LODEPNG_COMPILE_ENCODER
-/* Zlib-compress an unsigned char buffer */
-unsigned compress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize,
- const LodePNGCompressSettings& settings = lodepng_default_compress_settings);
-
-/* Zlib-compress an std::vector */
-unsigned compress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in,
- const LodePNGCompressSettings& settings = lodepng_default_compress_settings);
-#endif /* LODEPNG_COMPILE_ENCODER */
-#endif /* LODEPNG_COMPILE_ZLIB */
-} /* namespace lodepng */
-#endif /*LODEPNG_COMPILE_CPP*/
-
-/*
-TODO:
-[.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often
-[.] check compatibility with various compilers - done but needs to be redone for every newer version
-[X] converting color to 16-bit per channel types
-[X] support color profile chunk types (but never let them touch RGB values by default)
-[ ] support all public PNG chunk types (almost done except sPLT and hIST)
-[ ] make sure encoder generates no chunks with size > (2^31)-1
-[ ] partial decoding (stream processing)
-[X] let the "isFullyOpaque" function check color keys and transparent palettes too
-[X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl"
-[ ] allow treating some errors like warnings, when image is recoverable (e.g. 69, 57, 58)
-[ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ...
-[ ] error messages with line numbers (and version)
-[ ] errors in state instead of as return code?
-[ ] new errors/warnings like suspiciously big decompressed ztxt or iccp chunk
-[ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes
-[ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ...
-[ ] allow user to give data (void*) to custom allocator
-[X] provide alternatives for C library functions not present on some platforms (memcpy, ...)
-*/
-
-#endif /*LODEPNG_H inclusion guard*/
-
-/*
-LodePNG Documentation
----------------------
-
-0. table of contents
---------------------
-
- 1. about
- 1.1. supported features
- 1.2. features not supported
- 2. C and C++ version
- 3. security
- 4. decoding
- 5. encoding
- 6. color conversions
- 6.1. PNG color types
- 6.2. color conversions
- 6.3. padding bits
- 6.4. A note about 16-bits per channel and endianness
- 7. error values
- 8. chunks and PNG editing
- 9. compiler support
- 10. examples
- 10.1. decoder C++ example
- 10.2. decoder C example
- 11. state settings reference
- 12. changes
- 13. contact information
-
-
-1. about
---------
-
-PNG is a file format to store raster images losslessly with good compression,
-supporting different color types and alpha channel.
-
-LodePNG is a PNG codec according to the Portable Network Graphics (PNG)
-Specification (Second Edition) - W3C Recommendation 10 November 2003.
-
-The specifications used are:
-
-*) Portable Network Graphics (PNG) Specification (Second Edition):
- http://www.w3.org/TR/2003/REC-PNG-20031110
-*) RFC 1950 ZLIB Compressed Data Format version 3.3:
- http://www.gzip.org/zlib/rfc-zlib.html
-*) RFC 1951 DEFLATE Compressed Data Format Specification ver 1.3:
- http://www.gzip.org/zlib/rfc-deflate.html
-
-The most recent version of LodePNG can currently be found at
-http://lodev.org/lodepng/
-
-LodePNG works both in C (ISO C90) and C++, with a C++ wrapper that adds
-extra functionality.
-
-LodePNG exists out of two files:
--lodepng.h: the header file for both C and C++
--lodepng.c(pp): give it the name lodepng.c or lodepng.cpp (or .cc) depending on your usage
-
-If you want to start using LodePNG right away without reading this doc, get the
-examples from the LodePNG website to see how to use it in code, or check the
-smaller examples in chapter 13 here.
-
-LodePNG is simple but only supports the basic requirements. To achieve
-simplicity, the following design choices were made: There are no dependencies
-on any external library. There are functions to decode and encode a PNG with
-a single function call, and extended versions of these functions taking a
-LodePNGState struct allowing to specify or get more information. By default
-the colors of the raw image are always RGB or RGBA, no matter what color type
-the PNG file uses. To read and write files, there are simple functions to
-convert the files to/from buffers in memory.
-
-This all makes LodePNG suitable for loading textures in games, demos and small
-programs, ... It's less suitable for full fledged image editors, loading PNGs
-over network (it requires all the image data to be available before decoding can
-begin), life-critical systems, ...
-
-1.1. supported features
------------------------
-
-The following features are supported by the decoder:
-
-*) decoding of PNGs with any color type, bit depth and interlace mode, to a 24- or 32-bit color raw image,
- or the same color type as the PNG
-*) encoding of PNGs, from any raw image to 24- or 32-bit color, or the same color type as the raw image
-*) Adam7 interlace and deinterlace for any color type
-*) loading the image from harddisk or decoding it from a buffer from other sources than harddisk
-*) support for alpha channels, including RGBA color model, translucent palettes and color keying
-*) zlib decompression (inflate)
-*) zlib compression (deflate)
-*) CRC32 and ADLER32 checksums
-*) colorimetric color profile conversions: currently experimentally available in lodepng_util.cpp only,
- plus alternatively ability to pass on chroma/gamma/ICC profile information to other color management system.
-*) handling of unknown chunks, allowing making a PNG editor that stores custom and unknown chunks.
-*) the following chunks are supported by both encoder and decoder:
- IHDR: header information
- PLTE: color palette
- IDAT: pixel data
- IEND: the final chunk
- tRNS: transparency for palettized images
- tEXt: textual information
- zTXt: compressed textual information
- iTXt: international textual information
- bKGD: suggested background color
- pHYs: physical dimensions
- tIME: modification time
- cHRM: RGB chromaticities
- gAMA: RGB gamma correction
- iCCP: ICC color profile
- sRGB: rendering intent
- sBIT: significant bits
-
-1.2. features not supported
----------------------------
-
-The following features are not (yet) supported:
-
-*) some features needed to make a conformant PNG-Editor might be still missing.
-*) partial loading/stream processing. All data must be available and is processed in one call.
-*) The hIST and sPLT public chunks are not (yet) supported but treated as unknown chunks
-
-
-2. C and C++ version
---------------------
-
-The C version uses buffers allocated with alloc that you need to free()
-yourself. You need to use init and cleanup functions for each struct whenever
-using a struct from the C version to avoid exploits and memory leaks.
-
-The C++ version has extra functions with std::vectors in the interface and the
-lodepng::State class which is a LodePNGState with constructor and destructor.
-
-These files work without modification for both C and C++ compilers because all
-the additional C++ code is in "#ifdef __cplusplus" blocks that make C-compilers
-ignore it, and the C code is made to compile both with strict ISO C90 and C++.
-
-To use the C++ version, you need to rename the source file to lodepng.cpp
-(instead of lodepng.c), and compile it with a C++ compiler.
-
-To use the C version, you need to rename the source file to lodepng.c (instead
-of lodepng.cpp), and compile it with a C compiler.
-
-
-3. Security
------------
-
-Even if carefully designed, it's always possible that LodePNG contains possible
-exploits. If you discover one, please let me know, and it will be fixed.
-
-When using LodePNG, care has to be taken with the C version of LodePNG, as well
-as the C-style structs when working with C++. The following conventions are used
-for all C-style structs:
-
--if a struct has a corresponding init function, always call the init function when making a new one
--if a struct has a corresponding cleanup function, call it before the struct disappears to avoid memory leaks
--if a struct has a corresponding copy function, use the copy function instead of "=".
- The destination must also be inited already.
-
-
-4. Decoding
------------
-
-Decoding converts a PNG compressed image to a raw pixel buffer.
-
-Most documentation on using the decoder is at its declarations in the header
-above. For C, simple decoding can be done with functions such as
-lodepng_decode32, and more advanced decoding can be done with the struct
-LodePNGState and lodepng_decode. For C++, all decoding can be done with the
-various lodepng::decode functions, and lodepng::State can be used for advanced
-features.
-
-When using the LodePNGState, it uses the following fields for decoding:
-*) LodePNGInfo info_png: it stores extra information about the PNG (the input) in here
-*) LodePNGColorMode info_raw: here you can say what color mode of the raw image (the output) you want to get
-*) LodePNGDecoderSettings decoder: you can specify a few extra settings for the decoder to use
-
-LodePNGInfo info_png
---------------------
-
-After decoding, this contains extra information of the PNG image, except the actual
-pixels, width and height because these are already gotten directly from the decoder
-functions.
-
-It contains for example the original color type of the PNG image, text comments,
-suggested background color, etc... More details about the LodePNGInfo struct are
-at its declaration documentation.
-
-LodePNGColorMode info_raw
--------------------------
-
-When decoding, here you can specify which color type you want
-the resulting raw image to be. If this is different from the colortype of the
-PNG, then the decoder will automatically convert the result. This conversion
-always works, except if you want it to convert a color PNG to grayscale or to
-a palette with missing colors.
-
-By default, 32-bit color is used for the result.
-
-LodePNGDecoderSettings decoder
-------------------------------
-
-The settings can be used to ignore the errors created by invalid CRC and Adler32
-chunks, and to disable the decoding of tEXt chunks.
-
-There's also a setting color_convert, true by default. If false, no conversion
-is done, the resulting data will be as it was in the PNG (after decompression)
-and you'll have to puzzle the colors of the pixels together yourself using the
-color type information in the LodePNGInfo.
-
-
-5. Encoding
------------
-
-Encoding converts a raw pixel buffer to a PNG compressed image.
-
-Most documentation on using the encoder is at its declarations in the header
-above. For C, simple encoding can be done with functions such as
-lodepng_encode32, and more advanced decoding can be done with the struct
-LodePNGState and lodepng_encode. For C++, all encoding can be done with the
-various lodepng::encode functions, and lodepng::State can be used for advanced
-features.
-
-Like the decoder, the encoder can also give errors. However it gives less errors
-since the encoder input is trusted, the decoder input (a PNG image that could
-be forged by anyone) is not trusted.
-
-When using the LodePNGState, it uses the following fields for encoding:
-*) LodePNGInfo info_png: here you specify how you want the PNG (the output) to be.
-*) LodePNGColorMode info_raw: here you say what color type of the raw image (the input) has
-*) LodePNGEncoderSettings encoder: you can specify a few settings for the encoder to use
-
-LodePNGInfo info_png
---------------------
-
-When encoding, you use this the opposite way as when decoding: for encoding,
-you fill in the values you want the PNG to have before encoding. By default it's
-not needed to specify a color type for the PNG since it's automatically chosen,
-but it's possible to choose it yourself given the right settings.
-
-The encoder will not always exactly match the LodePNGInfo struct you give,
-it tries as close as possible. Some things are ignored by the encoder. The
-encoder uses, for example, the following settings from it when applicable:
-colortype and bitdepth, text chunks, time chunk, the color key, the palette, the
-background color, the interlace method, unknown chunks, ...
-
-When encoding to a PNG with colortype 3, the encoder will generate a PLTE chunk.
-If the palette contains any colors for which the alpha channel is not 255 (so
-there are translucent colors in the palette), it'll add a tRNS chunk.
-
-LodePNGColorMode info_raw
--------------------------
-
-You specify the color type of the raw image that you give to the input here,
-including a possible transparent color key and palette you happen to be using in
-your raw image data.
-
-By default, 32-bit color is assumed, meaning your input has to be in RGBA
-format with 4 bytes (unsigned chars) per pixel.
-
-LodePNGEncoderSettings encoder
-------------------------------
-
-The following settings are supported (some are in sub-structs):
-*) auto_convert: when this option is enabled, the encoder will
-automatically choose the smallest possible color mode (including color key) that
-can encode the colors of all pixels without information loss.
-*) btype: the block type for LZ77. 0 = uncompressed, 1 = fixed huffman tree,
- 2 = dynamic huffman tree (best compression). Should be 2 for proper
- compression.
-*) use_lz77: whether or not to use LZ77 for compressed block types. Should be
- true for proper compression.
-*) windowsize: the window size used by the LZ77 encoder (1 - 32768). Has value
- 2048 by default, but can be set to 32768 for better, but slow, compression.
-*) force_palette: if colortype is 2 or 6, you can make the encoder write a PLTE
- chunk if force_palette is true. This can used as suggested palette to convert
- to by viewers that don't support more than 256 colors (if those still exist)
-*) add_id: add text chunk "Encoder: LodePNG <version>" to the image.
-*) text_compression: default 1. If 1, it'll store texts as zTXt instead of tEXt chunks.
- zTXt chunks use zlib compression on the text. This gives a smaller result on
- large texts but a larger result on small texts (such as a single program name).
- It's all tEXt or all zTXt though, there's no separate setting per text yet.
-
-
-6. color conversions
---------------------
-
-An important thing to note about LodePNG, is that the color type of the PNG, and
-the color type of the raw image, are completely independent. By default, when
-you decode a PNG, you get the result as a raw image in the color type you want,
-no matter whether the PNG was encoded with a palette, grayscale or RGBA color.
-And if you encode an image, by default LodePNG will automatically choose the PNG
-color type that gives good compression based on the values of colors and amount
-of colors in the image. It can be configured to let you control it instead as
-well, though.
-
-To be able to do this, LodePNG does conversions from one color mode to another.
-It can convert from almost any color type to any other color type, except the
-following conversions: RGB to grayscale is not supported, and converting to a
-palette when the palette doesn't have a required color is not supported. This is
-not supported on purpose: this is information loss which requires a color
-reduction algorithm that is beyond the scope of a PNG encoder (yes, RGB to gray
-is easy, but there are multiple ways if you want to give some channels more
-weight).
-
-By default, when decoding, you get the raw image in 32-bit RGBA or 24-bit RGB
-color, no matter what color type the PNG has. And by default when encoding,
-LodePNG automatically picks the best color model for the output PNG, and expects
-the input image to be 32-bit RGBA or 24-bit RGB. So, unless you want to control
-the color format of the images yourself, you can skip this chapter.
-
-6.1. PNG color types
---------------------
-
-A PNG image can have many color types, ranging from 1-bit color to 64-bit color,
-as well as palettized color modes. After the zlib decompression and unfiltering
-in the PNG image is done, the raw pixel data will have that color type and thus
-a certain amount of bits per pixel. If you want the output raw image after
-decoding to have another color type, a conversion is done by LodePNG.
-
-The PNG specification gives the following color types:
-
-0: grayscale, bit depths 1, 2, 4, 8, 16
-2: RGB, bit depths 8 and 16
-3: palette, bit depths 1, 2, 4 and 8
-4: grayscale with alpha, bit depths 8 and 16
-6: RGBA, bit depths 8 and 16
-
-Bit depth is the amount of bits per pixel per color channel. So the total amount
-of bits per pixel is: amount of channels * bitdepth.
-
-6.2. color conversions
-----------------------
-
-As explained in the sections about the encoder and decoder, you can specify
-color types and bit depths in info_png and info_raw to change the default
-behaviour.
-
-If, when decoding, you want the raw image to be something else than the default,
-you need to set the color type and bit depth you want in the LodePNGColorMode,
-or the parameters colortype and bitdepth of the simple decoding function.
-
-If, when encoding, you use another color type than the default in the raw input
-image, you need to specify its color type and bit depth in the LodePNGColorMode
-of the raw image, or use the parameters colortype and bitdepth of the simple
-encoding function.
-
-If, when encoding, you don't want LodePNG to choose the output PNG color type
-but control it yourself, you need to set auto_convert in the encoder settings
-to false, and specify the color type you want in the LodePNGInfo of the
-encoder (including palette: it can generate a palette if auto_convert is true,
-otherwise not).
-
-If the input and output color type differ (whether user chosen or auto chosen),
-LodePNG will do a color conversion, which follows the rules below, and may
-sometimes result in an error.
-
-To avoid some confusion:
--the decoder converts from PNG to raw image
--the encoder converts from raw image to PNG
--the colortype and bitdepth in LodePNGColorMode info_raw, are those of the raw image
--the colortype and bitdepth in the color field of LodePNGInfo info_png, are those of the PNG
--when encoding, the color type in LodePNGInfo is ignored if auto_convert
- is enabled, it is automatically generated instead
--when decoding, the color type in LodePNGInfo is set by the decoder to that of the original
- PNG image, but it can be ignored since the raw image has the color type you requested instead
--if the color type of the LodePNGColorMode and PNG image aren't the same, a conversion
- between the color types is done if the color types are supported. If it is not
- supported, an error is returned. If the types are the same, no conversion is done.
--even though some conversions aren't supported, LodePNG supports loading PNGs from any
- colortype and saving PNGs to any colortype, sometimes it just requires preparing
- the raw image correctly before encoding.
--both encoder and decoder use the same color converter.
-
-The function lodepng_convert does the color conversion. It is available in the
-interface but normally isn't needed since the encoder and decoder already call
-it.
-
-Non supported color conversions:
--color to grayscale when non-gray pixels are present: no error is thrown, but
-the result will look ugly because only the red channel is taken (it assumes all
-three channels are the same in this case so ignores green and blue). The reason
-no error is given is to allow converting from three-channel grayscale images to
-one-channel even if there are numerical imprecisions.
--anything to palette when the palette does not have an exact match for a from-color
-in it: in this case an error is thrown
-
-Supported color conversions:
--anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA
--any gray or gray+alpha, to gray or gray+alpha
--anything to a palette, as long as the palette has the requested colors in it
--removing alpha channel
--higher to smaller bitdepth, and vice versa
-
-If you want no color conversion to be done (e.g. for speed or control):
--In the encoder, you can make it save a PNG with any color type by giving the
-raw color mode and LodePNGInfo the same color mode, and setting auto_convert to
-false.
--In the decoder, you can make it store the pixel data in the same color type
-as the PNG has, by setting the color_convert setting to false. Settings in
-info_raw are then ignored.
-
-6.3. padding bits
------------------
-
-In the PNG file format, if a less than 8-bit per pixel color type is used and the scanlines
-have a bit amount that isn't a multiple of 8, then padding bits are used so that each
-scanline starts at a fresh byte. But that is NOT true for the LodePNG raw input and output.
-The raw input image you give to the encoder, and the raw output image you get from the decoder
-will NOT have these padding bits, e.g. in the case of a 1-bit image with a width
-of 7 pixels, the first pixel of the second scanline will the 8th bit of the first byte,
-not the first bit of a new byte.
-
-6.4. A note about 16-bits per channel and endianness
-----------------------------------------------------
-
-LodePNG uses unsigned char arrays for 16-bit per channel colors too, just like
-for any other color format. The 16-bit values are stored in big endian (most
-significant byte first) in these arrays. This is the opposite order of the
-little endian used by x86 CPU's.
-
-LodePNG always uses big endian because the PNG file format does so internally.
-Conversions to other formats than PNG uses internally are not supported by
-LodePNG on purpose, there are myriads of formats, including endianness of 16-bit
-colors, the order in which you store R, G, B and A, and so on. Supporting and
-converting to/from all that is outside the scope of LodePNG.
-
-This may mean that, depending on your use case, you may want to convert the big
-endian output of LodePNG to little endian with a for loop. This is certainly not
-always needed, many applications and libraries support big endian 16-bit colors
-anyway, but it means you cannot simply cast the unsigned char* buffer to an
-unsigned short* buffer on x86 CPUs.
-
-
-7. error values
----------------
-
-All functions in LodePNG that return an error code, return 0 if everything went
-OK, or a non-zero code if there was an error.
-
-The meaning of the LodePNG error values can be retrieved with the function
-lodepng_error_text: given the numerical error code, it returns a description
-of the error in English as a string.
-
-Check the implementation of lodepng_error_text to see the meaning of each code.
-
-It is not recommended to use the numerical values to programmatically make
-different decisions based on error types as the numbers are not guaranteed to
-stay backwards compatible. They are for human consumption only. Programmatically
-only 0 or non-0 matter.
-
-
-8. chunks and PNG editing
--------------------------
-
-If you want to add extra chunks to a PNG you encode, or use LodePNG for a PNG
-editor that should follow the rules about handling of unknown chunks, or if your
-program is able to read other types of chunks than the ones handled by LodePNG,
-then that's possible with the chunk functions of LodePNG.
-
-A PNG chunk has the following layout:
-
-4 bytes length
-4 bytes type name
-length bytes data
-4 bytes CRC
-
-8.1. iterating through chunks
------------------------------
-
-If you have a buffer containing the PNG image data, then the first chunk (the
-IHDR chunk) starts at byte number 8 of that buffer. The first 8 bytes are the
-signature of the PNG and are not part of a chunk. But if you start at byte 8
-then you have a chunk, and can check the following things of it.
-
-NOTE: none of these functions check for memory buffer boundaries. To avoid
-exploits, always make sure the buffer contains all the data of the chunks.
-When using lodepng_chunk_next, make sure the returned value is within the
-allocated memory.
-
-unsigned lodepng_chunk_length(const unsigned char* chunk):
-
-Get the length of the chunk's data. The total chunk length is this length + 12.
-
-void lodepng_chunk_type(char type[5], const unsigned char* chunk):
-unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type):
-
-Get the type of the chunk or compare if it's a certain type
-
-unsigned char lodepng_chunk_critical(const unsigned char* chunk):
-unsigned char lodepng_chunk_private(const unsigned char* chunk):
-unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk):
-
-Check if the chunk is critical in the PNG standard (only IHDR, PLTE, IDAT and IEND are).
-Check if the chunk is private (public chunks are part of the standard, private ones not).
-Check if the chunk is safe to copy. If it's not, then, when modifying data in a critical
-chunk, unsafe to copy chunks of the old image may NOT be saved in the new one if your
-program doesn't handle that type of unknown chunk.
-
-unsigned char* lodepng_chunk_data(unsigned char* chunk):
-const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk):
-
-Get a pointer to the start of the data of the chunk.
-
-unsigned lodepng_chunk_check_crc(const unsigned char* chunk):
-void lodepng_chunk_generate_crc(unsigned char* chunk):
-
-Check if the crc is correct or generate a correct one.
-
-unsigned char* lodepng_chunk_next(unsigned char* chunk):
-const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk):
-
-Iterate to the next chunk. This works if you have a buffer with consecutive chunks. Note that these
-functions do no boundary checking of the allocated data whatsoever, so make sure there is enough
-data available in the buffer to be able to go to the next chunk.
-
-unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk):
-unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length,
- const char* type, const unsigned char* data):
-
-These functions are used to create new chunks that are appended to the data in *out that has
-length *outsize. The append function appends an existing chunk to the new data. The create
-function creates a new chunk with the given parameters and appends it. Type is the 4-letter
-name of the chunk.
-
-8.2. chunks in info_png
------------------------
-
-The LodePNGInfo struct contains fields with the unknown chunk in it. It has 3
-buffers (each with size) to contain 3 types of unknown chunks:
-the ones that come before the PLTE chunk, the ones that come between the PLTE
-and the IDAT chunks, and the ones that come after the IDAT chunks.
-It's necessary to make the distinction between these 3 cases because the PNG
-standard forces to keep the ordering of unknown chunks compared to the critical
-chunks, but does not force any other ordering rules.
-
-info_png.unknown_chunks_data[0] is the chunks before PLTE
-info_png.unknown_chunks_data[1] is the chunks after PLTE, before IDAT
-info_png.unknown_chunks_data[2] is the chunks after IDAT
-
-The chunks in these 3 buffers can be iterated through and read by using the same
-way described in the previous subchapter.
-
-When using the decoder to decode a PNG, you can make it store all unknown chunks
-if you set the option settings.remember_unknown_chunks to 1. By default, this
-option is off (0).
-
-The encoder will always encode unknown chunks that are stored in the info_png.
-If you need it to add a particular chunk that isn't known by LodePNG, you can
-use lodepng_chunk_append or lodepng_chunk_create to the chunk data in
-info_png.unknown_chunks_data[x].
-
-Chunks that are known by LodePNG should not be added in that way. E.g. to make
-LodePNG add a bKGD chunk, set background_defined to true and add the correct
-parameters there instead.
-
-
-9. compiler support
--------------------
-
-No libraries other than the current standard C library are needed to compile
-LodePNG. For the C++ version, only the standard C++ library is needed on top.
-Add the files lodepng.c(pp) and lodepng.h to your project, include
-lodepng.h where needed, and your program can read/write PNG files.
-
-It is compatible with C90 and up, and C++03 and up.
-
-If performance is important, use optimization when compiling! For both the
-encoder and decoder, this makes a large difference.
-
-Make sure that LodePNG is compiled with the same compiler of the same version
-and with the same settings as the rest of the program, or the interfaces with
-std::vectors and std::strings in C++ can be incompatible.
-
-CHAR_BITS must be 8 or higher, because LodePNG uses unsigned chars for octets.
-
-*) gcc and g++
-
-LodePNG is developed in gcc so this compiler is natively supported. It gives no
-warnings with compiler options "-Wall -Wextra -pedantic -ansi", with gcc and g++
-version 4.7.1 on Linux, 32-bit and 64-bit.
-
-*) Clang
-
-Fully supported and warning-free.
-
-*) Mingw
-
-The Mingw compiler (a port of gcc for Windows) should be fully supported by
-LodePNG.
-
-*) Visual Studio and Visual C++ Express Edition
-
-LodePNG should be warning-free with warning level W4. Two warnings were disabled
-with pragmas though: warning 4244 about implicit conversions, and warning 4996
-where it wants to use a non-standard function fopen_s instead of the standard C
-fopen.
-
-Visual Studio may want "stdafx.h" files to be included in each source file and
-give an error "unexpected end of file while looking for precompiled header".
-This is not standard C++ and will not be added to the stock LodePNG. You can
-disable it for lodepng.cpp only by right clicking it, Properties, C/C++,
-Precompiled Headers, and set it to Not Using Precompiled Headers there.
-
-NOTE: Modern versions of VS should be fully supported, but old versions, e.g.
-VS6, are not guaranteed to work.
-
-*) Compilers on Macintosh
-
-LodePNG has been reported to work both with gcc and LLVM for Macintosh, both for
-C and C++.
-
-*) Other Compilers
-
-If you encounter problems on any compilers, feel free to let me know and I may
-try to fix it if the compiler is modern and standards compliant.
-
-
-10. examples
-------------
-
-This decoder example shows the most basic usage of LodePNG. More complex
-examples can be found on the LodePNG website.
-
-NOTE: these examples do not support wide-character filenames, you can use an
-external method to handle such files and encode or decode in-memory
-
-10.1. decoder C++ example
--------------------------
-
-#include "lodepng.h"
-#include <iostream>
-
-int main(int argc, char *argv[]) {
- const char* filename = argc > 1 ? argv[1] : "test.png";
-
- //load and decode
- std::vector<unsigned char> image;
- unsigned width, height;
- unsigned error = lodepng::decode(image, width, height, filename);
-
- //if there's an error, display it
- if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
-
- //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ...
-}
-
-10.2. decoder C example
------------------------
-
-#include "lodepng.h"
-
-int main(int argc, char *argv[]) {
- unsigned error;
- unsigned char* image;
- size_t width, height;
- const char* filename = argc > 1 ? argv[1] : "test.png";
-
- error = lodepng_decode32_file(&image, &width, &height, filename);
-
- if(error) printf("decoder error %u: %s\n", error, lodepng_error_text(error));
-
- / * use image here * /
-
- free(image);
- return 0;
-}
-
-11. state settings reference
-----------------------------
-
-A quick reference of some settings to set on the LodePNGState
-
-For decoding:
-
-state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums
-state.decoder.zlibsettings.custom_...: use custom inflate function
-state.decoder.ignore_crc: ignore CRC checksums
-state.decoder.ignore_critical: ignore unknown critical chunks
-state.decoder.ignore_end: ignore missing IEND chunk. May fail if this corruption causes other errors
-state.decoder.color_convert: convert internal PNG color to chosen one
-state.decoder.read_text_chunks: whether to read in text metadata chunks
-state.decoder.remember_unknown_chunks: whether to read in unknown chunks
-state.info_raw.colortype: desired color type for decoded image
-state.info_raw.bitdepth: desired bit depth for decoded image
-state.info_raw....: more color settings, see struct LodePNGColorMode
-state.info_png....: no settings for decoder but ouput, see struct LodePNGInfo
-
-For encoding:
-
-state.encoder.zlibsettings.btype: disable compression by setting it to 0
-state.encoder.zlibsettings.use_lz77: use LZ77 in compression
-state.encoder.zlibsettings.windowsize: tweak LZ77 windowsize
-state.encoder.zlibsettings.minmatch: tweak min LZ77 length to match
-state.encoder.zlibsettings.nicematch: tweak LZ77 match where to stop searching
-state.encoder.zlibsettings.lazymatching: try one more LZ77 matching
-state.encoder.zlibsettings.custom_...: use custom deflate function
-state.encoder.auto_convert: choose optimal PNG color type, if 0 uses info_png
-state.encoder.filter_palette_zero: PNG filter strategy for palette
-state.encoder.filter_strategy: PNG filter strategy to encode with
-state.encoder.force_palette: add palette even if not encoding to one
-state.encoder.add_id: add LodePNG identifier and version as a text chunk
-state.encoder.text_compression: use compressed text chunks for metadata
-state.info_raw.colortype: color type of raw input image you provide
-state.info_raw.bitdepth: bit depth of raw input image you provide
-state.info_raw: more color settings, see struct LodePNGColorMode
-state.info_png.color.colortype: desired color type if auto_convert is false
-state.info_png.color.bitdepth: desired bit depth if auto_convert is false
-state.info_png.color....: more color settings, see struct LodePNGColorMode
-state.info_png....: more PNG related settings, see struct LodePNGInfo
-
-
-12. changes
------------
-
-The version number of LodePNG is the date of the change given in the format
-yyyymmdd.
-
-Some changes aren't backwards compatible. Those are indicated with a (!)
-symbol.
-
-Not all changes are listed here, the commit history in github lists more:
-https://github.com/lvandeve/lodepng
-
-*) 10 apr 2023: faster CRC32 implementation, but with larger lookup table.
-*) 13 jun 2022: added support for the sBIT chunk.
-*) 09 jan 2022: minor decoder speed improvements.
-*) 27 jun 2021: added warnings that file reading/writing functions don't support
- wide-character filenames (support for this is not planned, opening files is
- not the core part of PNG decoding/decoding and is platform dependent).
-*) 17 okt 2020: prevent decoding too large text/icc chunks by default.
-*) 06 mar 2020: simplified some of the dynamic memory allocations.
-*) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct
- overflow checks.
-*) 14 aug 2019: around 25% faster decoding thanks to huffman lookup tables.
-*) 15 jun 2019: (!) auto_choose_color API changed (for bugfix: don't use palette
- if gray ICC profile) and non-ICC LodePNGColorProfile renamed to
- LodePNGColorStats.
-*) 30 dec 2018: code style changes only: removed newlines before opening braces.
-*) 10 sep 2018: added way to inspect metadata chunks without full decoding.
-*) 19 aug 2018: (!) fixed color mode bKGD is encoded with and made it use
- palette index in case of palette.
-*) 10 aug 2018: (!) added support for gAMA, cHRM, sRGB and iCCP chunks. This
- change is backwards compatible unless you relied on unknown_chunks for those.
-*) 11 jun 2018: less restrictive check for pixel size integer overflow
-*) 14 jan 2018: allow optionally ignoring a few more recoverable errors
-*) 17 sep 2017: fix memory leak for some encoder input error cases
-*) 27 nov 2016: grey+alpha auto color model detection bugfix
-*) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort).
-*) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within
- the limits of pure C90).
-*) 08 dec 2015: Made load_file function return error if file can't be opened.
-*) 24 okt 2015: Bugfix with decoding to palette output.
-*) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding.
-*) 24 aug 2014: Moved to github
-*) 23 aug 2014: Reduced needless memory usage of decoder.
-*) 28 jun 2014: Removed fix_png setting, always support palette OOB for
- simplicity. Made ColorProfile public.
-*) 09 jun 2014: Faster encoder by fixing hash bug and more zeros optimization.
-*) 22 dec 2013: Power of two windowsize required for optimization.
-*) 15 apr 2013: Fixed bug with LAC_ALPHA and color key.
-*) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png).
-*) 11 mar 2013: (!) Bugfix with custom free. Changed from "my" to "lodepng_"
- prefix for the custom allocators and made it possible with a new #define to
- use custom ones in your project without needing to change lodepng's code.
-*) 28 jan 2013: Bugfix with color key.
-*) 27 okt 2012: Tweaks in text chunk keyword length error handling.
-*) 8 okt 2012: (!) Added new filter strategy (entropy) and new auto color mode.
- (no palette). Better deflate tree encoding. New compression tweak settings.
- Faster color conversions while decoding. Some internal cleanups.
-*) 23 sep 2012: Reduced warnings in Visual Studio a little bit.
-*) 1 sep 2012: (!) Removed #define's for giving custom (de)compression functions
- and made it work with function pointers instead.
-*) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc
- and free functions and toggle #defines from compiler flags. Small fixes.
-*) 6 may 2012: (!) Made plugging in custom zlib/deflate functions more flexible.
-*) 22 apr 2012: (!) Made interface more consistent, renaming a lot. Removed
- redundant C++ codec classes. Reduced amount of structs. Everything changed,
- but it is cleaner now imho and functionality remains the same. Also fixed
- several bugs and shrunk the implementation code. Made new samples.
-*) 6 nov 2011: (!) By default, the encoder now automatically chooses the best
- PNG color model and bit depth, based on the amount and type of colors of the
- raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color.
-*) 9 okt 2011: simpler hash chain implementation for the encoder.
-*) 8 sep 2011: lz77 encoder lazy matching instead of greedy matching.
-*) 23 aug 2011: tweaked the zlib compression parameters after benchmarking.
- A bug with the PNG filtertype heuristic was fixed, so that it chooses much
- better ones (it's quite significant). A setting to do an experimental, slow,
- brute force search for PNG filter types is added.
-*) 17 aug 2011: (!) changed some C zlib related function names.
-*) 16 aug 2011: made the code less wide (max 120 characters per line).
-*) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors.
-*) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled.
-*) 11 dec 2010: encoding is made faster, based on suggestion by Peter Eastman
- to optimize long sequences of zeros.
-*) 13 nov 2010: added LodePNG_InfoColor_hasPaletteAlpha and
- LodePNG_InfoColor_canHaveAlpha functions for convenience.
-*) 7 nov 2010: added LodePNG_error_text function to get error code description.
-*) 30 okt 2010: made decoding slightly faster
-*) 26 okt 2010: (!) changed some C function and struct names (more consistent).
- Reorganized the documentation and the declaration order in the header.
-*) 08 aug 2010: only changed some comments and external samples.
-*) 05 jul 2010: fixed bug thanks to warnings in the new gcc version.
-*) 14 mar 2010: fixed bug where too much memory was allocated for char buffers.
-*) 02 sep 2008: fixed bug where it could create empty tree that linux apps could
- read by ignoring the problem but windows apps couldn't.
-*) 06 jun 2008: added more error checks for out of memory cases.
-*) 26 apr 2008: added a few more checks here and there to ensure more safety.
-*) 06 mar 2008: crash with encoding of strings fixed
-*) 02 feb 2008: support for international text chunks added (iTXt)
-*) 23 jan 2008: small cleanups, and #defines to divide code in sections
-*) 20 jan 2008: support for unknown chunks allowing using LodePNG for an editor.
-*) 18 jan 2008: support for tIME and pHYs chunks added to encoder and decoder.
-*) 17 jan 2008: ability to encode and decode compressed zTXt chunks added
- Also various fixes, such as in the deflate and the padding bits code.
-*) 13 jan 2008: Added ability to encode Adam7-interlaced images. Improved
- filtering code of encoder.
-*) 07 jan 2008: (!) changed LodePNG to use ISO C90 instead of C++. A
- C++ wrapper around this provides an interface almost identical to before.
- Having LodePNG be pure ISO C90 makes it more portable. The C and C++ code
- are together in these files but it works both for C and C++ compilers.
-*) 29 dec 2007: (!) changed most integer types to unsigned int + other tweaks
-*) 30 aug 2007: bug fixed which makes this Borland C++ compatible
-*) 09 aug 2007: some VS2005 warnings removed again
-*) 21 jul 2007: deflate code placed in new namespace separate from zlib code
-*) 08 jun 2007: fixed bug with 2- and 4-bit color, and small interlaced images
-*) 04 jun 2007: improved support for Visual Studio 2005: crash with accessing
- invalid std::vector element [0] fixed, and level 3 and 4 warnings removed
-*) 02 jun 2007: made the encoder add a tag with version by default
-*) 27 may 2007: zlib and png code separated (but still in the same file),
- simple encoder/decoder functions added for more simple usage cases
-*) 19 may 2007: minor fixes, some code cleaning, new error added (error 69),
- moved some examples from here to lodepng_examples.cpp
-*) 12 may 2007: palette decoding bug fixed
-*) 24 apr 2007: changed the license from BSD to the zlib license
-*) 11 mar 2007: very simple addition: ability to encode bKGD chunks.
-*) 04 mar 2007: (!) tEXt chunk related fixes, and support for encoding
- palettized PNG images. Plus little interface change with palette and texts.
-*) 03 mar 2007: Made it encode dynamic Huffman shorter with repeat codes.
- Fixed a bug where the end code of a block had length 0 in the Huffman tree.
-*) 26 feb 2007: Huffman compression with dynamic trees (BTYPE 2) now implemented
- and supported by the encoder, resulting in smaller PNGs at the output.
-*) 27 jan 2007: Made the Adler-32 test faster so that a timewaste is gone.
-*) 24 jan 2007: gave encoder an error interface. Added color conversion from any
- greyscale type to 8-bit greyscale with or without alpha.
-*) 21 jan 2007: (!) Totally changed the interface. It allows more color types
- to convert to and is more uniform. See the manual for how it works now.
-*) 07 jan 2007: Some cleanup & fixes, and a few changes over the last days:
- encode/decode custom tEXt chunks, separate classes for zlib & deflate, and
- at last made the decoder give errors for incorrect Adler32 or Crc.
-*) 01 jan 2007: Fixed bug with encoding PNGs with less than 8 bits per channel.
-*) 29 dec 2006: Added support for encoding images without alpha channel, and
- cleaned out code as well as making certain parts faster.
-*) 28 dec 2006: Added "Settings" to the encoder.
-*) 26 dec 2006: The encoder now does LZ77 encoding and produces much smaller files now.
- Removed some code duplication in the decoder. Fixed little bug in an example.
-*) 09 dec 2006: (!) Placed output parameters of public functions as first parameter.
- Fixed a bug of the decoder with 16-bit per color.
-*) 15 okt 2006: Changed documentation structure
-*) 09 okt 2006: Encoder class added. It encodes a valid PNG image from the
- given image buffer, however for now it's not compressed.
-*) 08 sep 2006: (!) Changed to interface with a Decoder class
-*) 30 jul 2006: (!) LodePNG_InfoPng , width and height are now retrieved in different
- way. Renamed decodePNG to decodePNGGeneric.
-*) 29 jul 2006: (!) Changed the interface: image info is now returned as a
- struct of type LodePNG::LodePNG_Info, instead of a vector, which was a bit clumsy.
-*) 28 jul 2006: Cleaned the code and added new error checks.
- Corrected terminology "deflate" into "inflate".
-*) 23 jun 2006: Added SDL example in the documentation in the header, this
- example allows easy debugging by displaying the PNG and its transparency.
-*) 22 jun 2006: (!) Changed way to obtain error value. Added
- loadFile function for convenience. Made decodePNG32 faster.
-*) 21 jun 2006: (!) Changed type of info vector to unsigned.
- Changed position of palette in info vector. Fixed an important bug that
- happened on PNGs with an uncompressed block.
-*) 16 jun 2006: Internally changed unsigned into unsigned where
- needed, and performed some optimizations.
-*) 07 jun 2006: (!) Renamed functions to decodePNG and placed them
- in LodePNG namespace. Changed the order of the parameters. Rewrote the
- documentation in the header. Renamed files to lodepng.cpp and lodepng.h
-*) 22 apr 2006: Optimized and improved some code
-*) 07 sep 2005: (!) Changed to std::vector interface
-*) 12 aug 2005: Initial release (C++, decoder only)
-
-
-13. contact information
------------------------
-
-Feel free to contact me with suggestions, problems, comments, ... concerning
-LodePNG. If you encounter a PNG image that doesn't work properly with this
-decoder, feel free to send it and I'll use it to find and fix the problem.
-
-My email address is (puzzle the account and domain together with an @ symbol):
-Domain: gmail dot com.
-Account: lode dot vandevenne.
-
-
-Copyright (c) 2005-2022 Lode Vandevenne
-*/
diff --git a/log.c b/log.c
deleted file mode 100644
index 727182c55e48..000000000000
--- a/log.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (c) 2017 rxi
- *
- * 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 <pthread.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include "log.h"
-
-#ifdef WIN32
-#include <windows.h>
-#endif
-
-pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
-
-static struct {
- FILE* fp;
- int level;
- int quiet;
-} L;
-
-static const char* level_names[] = {
- "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
-};
-
-#ifdef LOG_USE_COLOR
-#ifdef WIN32
-static const WORD level_colors[] = {
- 10, 11, 10, 6, 12, 12
-};
-#else
-static const char* level_colors[] = {
- "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m"
-};
-#endif
-#endif
-
-static void lock(void) {
- pthread_mutex_lock(&m);
-}
-
-static void unlock(void) {
- pthread_mutex_unlock(&m);
-}
-
-void log_set_fp(FILE* fp) {
- L.fp = fp;
-}
-
-void log_set_level(int level) {
- L.level = level;
-}
-
-void log_set_quiet(int enable) {
- L.quiet = enable ? 1 : 0;
-}
-
-void log_log(int level, const char* file, int line, const char* fmt, ...) {
- if(level < L.level)
- return;
-
- /* Get current time */
- time_t t = time(NULL);
- struct tm* lt = localtime(&t);
-
- /* Acquire lock */
- lock();
-
- /* Log to stderr */
- if(!L.quiet) {
- va_list args;
- char buf[16];
- buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0';
-
- #ifdef LOG_USE_COLOR
- #ifdef WIN32
- HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
- CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
- WORD saved_attributes;
-
- GetConsoleScreenBufferInfo(hConsole, &consoleInfo);
- saved_attributes = consoleInfo.wAttributes;
-
- // Force set to grey for this purpose, but keep the previous colour settings and revert
- // back to them after the logging has completed, since the user who may have changed colours
- // for their own logging prior to log_log called
- SetConsoleTextAttribute(hConsole, 7);
- fprintf(stderr, "%s ", buf);
- SetConsoleTextAttribute(hConsole, level_colors[level]);
- fprintf(stderr, "%s", level_names[level]);
-
- if (strlen(level_names[level]) == 5) {
- fprintf(stderr, " ");
- } else {
- fprintf(stderr, " ");
- }
- SetConsoleTextAttribute(hConsole, 8 /*GREY*/);
- fprintf(stderr, "%s:%d: ", file, line);
-
- // Revert back colour settings
- SetConsoleTextAttribute(hConsole, saved_attributes);
- #else
- fprintf(
- stderr, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ",
- buf, level_colors[level], level_names[level], file, line);
- #endif
- #else
- fprintf(stderr, "%s %-5s %s:%d: ", buf, level_names[level], file, line);
- #endif
- va_start(args, fmt);
- vfprintf(stderr, fmt, args);
- va_end(args);
- fprintf(stderr, "\n");
- }
-
- /* Log to file */
- if(L.fp) {
- va_list args;
- char buf[32];
- buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0';
- fprintf(L.fp, "%s %-5s %s:%d: ", buf, level_names[level], file, line);
- va_start(args, fmt);
- vfprintf(L.fp, fmt, args);
- va_end(args);
- fprintf(L.fp, "\n");
- }
-
- /* Release lock */
- unlock();
-}
diff --git a/log.h b/log.h
deleted file mode 100644
index 16a72516b6e9..000000000000
--- a/log.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * Copyright (c) 2017 rxi
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the MIT license. See `log.c` for details.
- */
-
-#ifndef LOG_H
-#define LOG_H
-
-#define __FILENAME__ \
- (__builtin_strrchr(__FILE__, '/') \
- ? (__builtin_strrchr(__FILE__, '/') + 1) \
- : (__builtin_strrchr(__FILE__, '\\') \
- ? __builtin_strrchr(__FILE__, '\\') + 1 \
- : __FILE__))
-
-#include <stdarg.h>
-#include <stdio.h>
-
-#define LOG_VERSION "0.1.0"
-
-typedef void (*log_LockFn)(void *udata, int lock);
-
-enum { LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL };
-
-#define log_trace(...) log_log(LOG_TRACE, __FILENAME__, __LINE__, __VA_ARGS__)
-#define log_debug(...) log_log(LOG_DEBUG, __FILENAME__, __LINE__, __VA_ARGS__)
-#define log_info(...) log_log(LOG_INFO, __FILENAME__, __LINE__, __VA_ARGS__)
-#define log_warn(...) log_log(LOG_WARN, __FILENAME__, __LINE__, __VA_ARGS__)
-#define log_error(...) log_log(LOG_ERROR, __FILENAME__, __LINE__, __VA_ARGS__)
-#define log_fatal(...) log_log(LOG_FATAL, __FILENAME__, __LINE__, __VA_ARGS__)
-
-void log_set_udata(void *udata);
-void log_set_lock(log_LockFn fn);
-void log_set_fp(FILE *fp);
-void log_set_level(int level);
-void log_set_quiet(int enable);
-
-void log_log(int level, const char *file, int line, const char *fmt, ...);
-
-#endif
diff --git a/microui.c b/microui.c
deleted file mode 100644
index a8a3f74a217d..000000000000
--- a/microui.c
+++ /dev/null
@@ -1,1208 +0,0 @@
-/*
-** Copyright (c) 2020 rxi
-**
-** 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "microui.h"
-
-#define unused(x) ((void) (x))
-
-#define expect(x) do { \
- if (!(x)) { \
- fprintf(stderr, "Fatal error: %s:%d: assertion '%s' failed\n", \
- __FILE__, __LINE__, #x); \
- abort(); \
- } \
- } while (0)
-
-#define push(stk, val) do { \
- expect((stk).idx < (int) (sizeof((stk).items) / sizeof(*(stk).items))); \
- (stk).items[(stk).idx] = (val); \
- (stk).idx++; /* incremented after incase `val` uses this value */ \
- } while (0)
-
-#define pop(stk) do { \
- expect((stk).idx > 0); \
- (stk).idx--; \
- } while (0)
-
-
-static mu_Rect unclipped_rect = { 0, 0, 0x1000000, 0x1000000 };
-
-static mu_Style default_style = {
- /* font | size | padding | spacing | indent */
- NULL, { 68, 10 }, 5, 4, 24,
- /* title_height | scrollbar_size | thumb_size */
- 24, 12, 8,
- {
- { 230, 230, 230, 255 }, /* MU_COLOR_TEXT */
- { 25, 25, 25, 255 }, /* MU_COLOR_BORDER */
- { 50, 50, 50, 255 }, /* MU_COLOR_WINDOWBG */
- { 25, 25, 25, 255 }, /* MU_COLOR_TITLEBG */
- { 240, 240, 240, 255 }, /* MU_COLOR_TITLETEXT */
- { 0, 0, 0, 0 }, /* MU_COLOR_PANELBG */
- { 75, 75, 75, 255 }, /* MU_COLOR_BUTTON */
- { 95, 95, 95, 255 }, /* MU_COLOR_BUTTONHOVER */
- { 115, 115, 115, 255 }, /* MU_COLOR_BUTTONFOCUS */
- { 30, 30, 30, 255 }, /* MU_COLOR_BASE */
- { 35, 35, 35, 255 }, /* MU_COLOR_BASEHOVER */
- { 40, 40, 40, 255 }, /* MU_COLOR_BASEFOCUS */
- { 43, 43, 43, 255 }, /* MU_COLOR_SCROLLBASE */
- { 30, 30, 30, 255 } /* MU_COLOR_SCROLLTHUMB */
- }
-};
-
-
-mu_Vec2 mu_vec2(int x, int y) {
- mu_Vec2 res;
- res.x = x; res.y = y;
- return res;
-}
-
-
-mu_Rect mu_rect(int x, int y, int w, int h) {
- mu_Rect res;
- res.x = x; res.y = y; res.w = w; res.h = h;
- return res;
-}
-
-
-mu_Color mu_color(int r, int g, int b, int a) {
- mu_Color res;
- res.r = r; res.g = g; res.b = b; res.a = a;
- return res;
-}
-
-
-static mu_Rect expand_rect(mu_Rect rect, int n) {
- return mu_rect(rect.x - n, rect.y - n, rect.w + n * 2, rect.h + n * 2);
-}
-
-
-static mu_Rect intersect_rects(mu_Rect r1, mu_Rect r2) {
- int x1 = mu_max(r1.x, r2.x);
- int y1 = mu_max(r1.y, r2.y);
- int x2 = mu_min(r1.x + r1.w, r2.x + r2.w);
- int y2 = mu_min(r1.y + r1.h, r2.y + r2.h);
- if (x2 < x1) { x2 = x1; }
- if (y2 < y1) { y2 = y1; }
- return mu_rect(x1, y1, x2 - x1, y2 - y1);
-}
-
-
-static int rect_overlaps_vec2(mu_Rect r, mu_Vec2 p) {
- return p.x >= r.x && p.x < r.x + r.w && p.y >= r.y && p.y < r.y + r.h;
-}
-
-
-static void draw_frame(mu_Context *ctx, mu_Rect rect, int colorid) {
- mu_draw_rect(ctx, rect, ctx->style->colors[colorid]);
- if (colorid == MU_COLOR_SCROLLBASE ||
- colorid == MU_COLOR_SCROLLTHUMB ||
- colorid == MU_COLOR_TITLEBG) { return; }
- /* draw border */
- if (ctx->style->colors[MU_COLOR_BORDER].a) {
- mu_draw_box(ctx, expand_rect(rect, 1), ctx->style->colors[MU_COLOR_BORDER]);
- }
-}
-
-
-void mu_init(mu_Context *ctx) {
- memset(ctx, 0, sizeof(*ctx));
- ctx->draw_frame = draw_frame;
- ctx->_style = default_style;
- ctx->style = &ctx->_style;
-}
-
-
-void mu_begin(mu_Context *ctx) {
- expect(ctx->text_width && ctx->text_height);
- ctx->command_list.idx = 0;
- ctx->root_list.idx = 0;
- ctx->scroll_target = NULL;
- ctx->hover_root = ctx->next_hover_root;
- ctx->next_hover_root = NULL;
- ctx->mouse_delta.x = ctx->mouse_pos.x - ctx->last_mouse_pos.x;
- ctx->mouse_delta.y = ctx->mouse_pos.y - ctx->last_mouse_pos.y;
- ctx->frame++;
-}
-
-
-static int compare_zindex(const void *a, const void *b) {
- return (*(mu_Container**) a)->zindex - (*(mu_Container**) b)->zindex;
-}
-
-
-void mu_end(mu_Context *ctx) {
- int i, n;
- /* check stacks */
- expect(ctx->container_stack.idx == 0);
- expect(ctx->clip_stack.idx == 0);
- expect(ctx->id_stack.idx == 0);
- expect(ctx->layout_stack.idx == 0);
-
- /* handle scroll input */
- if (ctx->scroll_target) {
- ctx->scroll_target->scroll.x += ctx->scroll_delta.x;
- ctx->scroll_target->scroll.y += ctx->scroll_delta.y;
- }
-
- /* unset focus if focus id was not touched this frame */
- if (!ctx->updated_focus) { ctx->focus = 0; }
- ctx->updated_focus = 0;
-
- /* bring hover root to front if mouse was pressed */
- if (ctx->mouse_pressed && ctx->next_hover_root &&
- ctx->next_hover_root->zindex < ctx->last_zindex &&
- ctx->next_hover_root->zindex >= 0
- ) {
- mu_bring_to_front(ctx, ctx->next_hover_root);
- }
-
- /* reset input state */
- ctx->key_pressed = 0;
- ctx->input_text[0] = '\0';
- ctx->mouse_pressed = 0;
- ctx->scroll_delta = mu_vec2(0, 0);
- ctx->last_mouse_pos = ctx->mouse_pos;
-
- /* sort root containers by zindex */
- n = ctx->root_list.idx;
- qsort(ctx->root_list.items, n, sizeof(mu_Container*), compare_zindex);
-
- /* set root container jump commands */
- for (i = 0; i < n; i++) {
- mu_Container *cnt = ctx->root_list.items[i];
- /* if this is the first container then make the first command jump to it.
- ** otherwise set the previous container's tail to jump to this one */
- if (i == 0) {
- mu_Command *cmd = (mu_Command*) ctx->command_list.items;
- cmd->jump.dst = (char*) cnt->head + sizeof(mu_JumpCommand);
- } else {
- mu_Container *prev = ctx->root_list.items[i - 1];
- prev->tail->jump.dst = (char*) cnt->head + sizeof(mu_JumpCommand);
- }
- /* make the last container's tail jump to the end of command list */
- if (i == n - 1) {
- cnt->tail->jump.dst = ctx->command_list.items + ctx->command_list.idx;
- }
- }
-}
-
-
-void mu_set_focus(mu_Context *ctx, mu_Id id) {
- ctx->focus = id;
- ctx->updated_focus = 1;
-}
-
-
-/* 32bit fnv-1a hash */
-#define HASH_INITIAL 2166136261
-
-static void hash(mu_Id *hash, const void *data, int size) {
- const unsigned char *p = data;
- while (size--) {
- *hash = (*hash ^ *p++) * 16777619;
- }
-}
-
-
-mu_Id mu_get_id(mu_Context *ctx, const void *data, int size) {
- int idx = ctx->id_stack.idx;
- mu_Id res = (idx > 0) ? ctx->id_stack.items[idx - 1] : HASH_INITIAL;
- hash(&res, data, size);
- ctx->last_id = res;
- return res;
-}
-
-
-void mu_push_id(mu_Context *ctx, const void *data, int size) {
- push(ctx->id_stack, mu_get_id(ctx, data, size));
-}
-
-
-void mu_pop_id(mu_Context *ctx) {
- pop(ctx->id_stack);
-}
-
-
-void mu_push_clip_rect(mu_Context *ctx, mu_Rect rect) {
- mu_Rect last = mu_get_clip_rect(ctx);
- push(ctx->clip_stack, intersect_rects(rect, last));
-}
-
-
-void mu_pop_clip_rect(mu_Context *ctx) {
- pop(ctx->clip_stack);
-}
-
-
-mu_Rect mu_get_clip_rect(mu_Context *ctx) {
- expect(ctx->clip_stack.idx > 0);
- return ctx->clip_stack.items[ctx->clip_stack.idx - 1];
-}
-
-
-int mu_check_clip(mu_Context *ctx, mu_Rect r) {
- mu_Rect cr = mu_get_clip_rect(ctx);
- if (r.x > cr.x + cr.w || r.x + r.w < cr.x ||
- r.y > cr.y + cr.h || r.y + r.h < cr.y ) { return MU_CLIP_ALL; }
- if (r.x >= cr.x && r.x + r.w <= cr.x + cr.w &&
- r.y >= cr.y && r.y + r.h <= cr.y + cr.h ) { return 0; }
- return MU_CLIP_PART;
-}
-
-
-static void push_layout(mu_Context *ctx, mu_Rect body, mu_Vec2 scroll) {
- mu_Layout layout;
- int width = 0;
- memset(&layout, 0, sizeof(layout));
- layout.body = mu_rect(body.x - scroll.x, body.y - scroll.y, body.w, body.h);
- layout.max = mu_vec2(-0x1000000, -0x1000000);
- push(ctx->layout_stack, layout);
- mu_layout_row(ctx, 1, &width, 0);
-}
-
-
-static mu_Layout* get_layout(mu_Context *ctx) {
- return &ctx->layout_stack.items[ctx->layout_stack.idx - 1];
-}
-
-
-static void pop_container(mu_Context *ctx) {
- mu_Container *cnt = mu_get_current_container(ctx);
- mu_Layout *layout = get_layout(ctx);
- cnt->content_size.x = layout->max.x - layout->body.x;
- cnt->content_size.y = layout->max.y - layout->body.y;
- /* pop container, layout and id */
- pop(ctx->container_stack);
- pop(ctx->layout_stack);
- mu_pop_id(ctx);
-}
-
-
-mu_Container* mu_get_current_container(mu_Context *ctx) {
- expect(ctx->container_stack.idx > 0);
- return ctx->container_stack.items[ ctx->container_stack.idx - 1 ];
-}
-
-
-static mu_Container* get_container(mu_Context *ctx, mu_Id id, int opt) {
- mu_Container *cnt;
- /* try to get existing container from pool */
- int idx = mu_pool_get(ctx, ctx->container_pool, MU_CONTAINERPOOL_SIZE, id);
- if (idx >= 0) {
- if (ctx->containers[idx].open || ~opt & MU_OPT_CLOSED) {
- mu_pool_update(ctx, ctx->container_pool, idx);
- }
- return &ctx->containers[idx];
- }
- if (opt & MU_OPT_CLOSED) { return NULL; }
- /* container not found in pool: init new container */
- idx = mu_pool_init(ctx, ctx->container_pool, MU_CONTAINERPOOL_SIZE, id);
- cnt = &ctx->containers[idx];
- memset(cnt, 0, sizeof(*cnt));
- cnt->open = 1;
- mu_bring_to_front(ctx, cnt);
- return cnt;
-}
-
-
-mu_Container* mu_get_container(mu_Context *ctx, const char *name) {
- mu_Id id = mu_get_id(ctx, name, strlen(name));
- return get_container(ctx, id, 0);
-}
-
-
-void mu_bring_to_front(mu_Context *ctx, mu_Container *cnt) {
- cnt->zindex = ++ctx->last_zindex;
-}
-
-
-/*============================================================================
-** pool
-**============================================================================*/
-
-int mu_pool_init(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id) {
- int i, n = -1, f = ctx->frame;
- for (i = 0; i < len; i++) {
- if (items[i].last_update < f) {
- f = items[i].last_update;
- n = i;
- }
- }
- expect(n > -1);
- items[n].id = id;
- mu_pool_update(ctx, items, n);
- return n;
-}
-
-
-int mu_pool_get(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id) {
- int i;
- unused(ctx);
- for (i = 0; i < len; i++) {
- if (items[i].id == id) { return i; }
- }
- return -1;
-}
-
-
-void mu_pool_update(mu_Context *ctx, mu_PoolItem *items, int idx) {
- items[idx].last_update = ctx->frame;
-}
-
-
-/*============================================================================
-** input handlers
-**============================================================================*/
-
-void mu_input_mousemove(mu_Context *ctx, int x, int y) {
- ctx->mouse_pos = mu_vec2(x, y);
-}
-
-
-void mu_input_mousedown(mu_Context *ctx, int x, int y, int btn) {
- mu_input_mousemove(ctx, x, y);
- ctx->mouse_down |= btn;
- ctx->mouse_pressed |= btn;
-}
-
-
-void mu_input_mouseup(mu_Context *ctx, int x, int y, int btn) {
- mu_input_mousemove(ctx, x, y);
- ctx->mouse_down &= ~btn;
-}
-
-
-void mu_input_scroll(mu_Context *ctx, int x, int y) {
- ctx->scroll_delta.x += x;
- ctx->scroll_delta.y += y;
-}
-
-
-void mu_input_keydown(mu_Context *ctx, int key) {
- ctx->key_pressed |= key;
- ctx->key_down |= key;
-}
-
-
-void mu_input_keyup(mu_Context *ctx, int key) {
- ctx->key_down &= ~key;
-}
-
-
-void mu_input_text(mu_Context *ctx, const char *text) {
- int len = strlen(ctx->input_text);
- int size = strlen(text) + 1;
- expect(len + size <= (int) sizeof(ctx->input_text));
- memcpy(ctx->input_text + len, text, size);
-}
-
-
-/*============================================================================
-** commandlist
-**============================================================================*/
-
-mu_Command* mu_push_command(mu_Context *ctx, int type, int size) {
- mu_Command *cmd = (mu_Command*) (ctx->command_list.items + ctx->command_list.idx);
- expect(ctx->command_list.idx + size < MU_COMMANDLIST_SIZE);
- cmd->base.type = type;
- cmd->base.size = size;
- ctx->command_list.idx += size;
- return cmd;
-}
-
-
-int mu_next_command(mu_Context *ctx, mu_Command **cmd) {
- if (*cmd) {
- *cmd = (mu_Command*) (((char*) *cmd) + (*cmd)->base.size);
- } else {
- *cmd = (mu_Command*) ctx->command_list.items;
- }
- while ((char*) *cmd != ctx->command_list.items + ctx->command_list.idx) {
- if ((*cmd)->type != MU_COMMAND_JUMP) { return 1; }
- *cmd = (*cmd)->jump.dst;
- }
- return 0;
-}
-
-
-static mu_Command* push_jump(mu_Context *ctx, mu_Command *dst) {
- mu_Command *cmd;
- cmd = mu_push_command(ctx, MU_COMMAND_JUMP, sizeof(mu_JumpCommand));
- cmd->jump.dst = dst;
- return cmd;
-}
-
-
-void mu_set_clip(mu_Context *ctx, mu_Rect rect) {
- mu_Command *cmd;
- cmd = mu_push_command(ctx, MU_COMMAND_CLIP, sizeof(mu_ClipCommand));
- cmd->clip.rect = rect;
-}
-
-
-void mu_draw_rect(mu_Context *ctx, mu_Rect rect, mu_Color color) {
- mu_Command *cmd;
- rect = intersect_rects(rect, mu_get_clip_rect(ctx));
- if (rect.w > 0 && rect.h > 0) {
- cmd = mu_push_command(ctx, MU_COMMAND_RECT, sizeof(mu_RectCommand));
- cmd->rect.rect = rect;
- cmd->rect.color = color;
- }
-}
-
-
-void mu_draw_box(mu_Context *ctx, mu_Rect rect, mu_Color color) {
- mu_draw_rect(ctx, mu_rect(rect.x + 1, rect.y, rect.w - 2, 1), color);
- mu_draw_rect(ctx, mu_rect(rect.x + 1, rect.y + rect.h - 1, rect.w - 2, 1), color);
- mu_draw_rect(ctx, mu_rect(rect.x, rect.y, 1, rect.h), color);
- mu_draw_rect(ctx, mu_rect(rect.x + rect.w - 1, rect.y, 1, rect.h), color);
-}
-
-
-void mu_draw_text(mu_Context *ctx, mu_Font font, const char *str, int len,
- mu_Vec2 pos, mu_Color color)
-{
- mu_Command *cmd;
- mu_Rect rect = mu_rect(
- pos.x, pos.y, ctx->text_width(font, str, len), ctx->text_height(font));
- int clipped = mu_check_clip(ctx, rect);
- if (clipped == MU_CLIP_ALL ) { return; }
- if (clipped == MU_CLIP_PART) { mu_set_clip(ctx, mu_get_clip_rect(ctx)); }
- /* add command */
- if (len < 0) { len = strlen(str); }
- cmd = mu_push_command(ctx, MU_COMMAND_TEXT, sizeof(mu_TextCommand) + len);
- memcpy(cmd->text.str, str, len);
- cmd->text.str[len] = '\0';
- cmd->text.pos = pos;
- cmd->text.color = color;
- cmd->text.font = font;
- /* reset clipping if it was set */
- if (clipped) { mu_set_clip(ctx, unclipped_rect); }
-}
-
-
-void mu_draw_icon(mu_Context *ctx, int id, mu_Rect rect, mu_Color color) {
- mu_Command *cmd;
- /* do clip command if the rect isn't fully contained within the cliprect */
- int clipped = mu_check_clip(ctx, rect);
- if (clipped == MU_CLIP_ALL ) { return; }
- if (clipped == MU_CLIP_PART) { mu_set_clip(ctx, mu_get_clip_rect(ctx)); }
- /* do icon command */
- cmd = mu_push_command(ctx, MU_COMMAND_ICON, sizeof(mu_IconCommand));
- cmd->icon.id = id;
- cmd->icon.rect = rect;
- cmd->icon.color = color;
- /* reset clipping if it was set */
- if (clipped) { mu_set_clip(ctx, unclipped_rect); }
-}
-
-
-/*============================================================================
-** layout
-**============================================================================*/
-
-enum { RELATIVE = 1, ABSOLUTE = 2 };
-
-
-void mu_layout_begin_column(mu_Context *ctx) {
- push_layout(ctx, mu_layout_next(ctx), mu_vec2(0, 0));
-}
-
-
-void mu_layout_end_column(mu_Context *ctx) {
- mu_Layout *a, *b;
- b = get_layout(ctx);
- pop(ctx->layout_stack);
- /* inherit position/next_row/max from child layout if they are greater */
- a = get_layout(ctx);
- a->position.x = mu_max(a->position.x, b->position.x + b->body.x - a->body.x);
- a->next_row = mu_max(a->next_row, b->next_row + b->body.y - a->body.y);
- a->max.x = mu_max(a->max.x, b->max.x);
- a->max.y = mu_max(a->max.y, b->max.y);
-}
-
-
-void mu_layout_row(mu_Context *ctx, int items, const int *widths, int height) {
- mu_Layout *layout = get_layout(ctx);
- if (widths) {
- expect(items <= MU_MAX_WIDTHS);
- memcpy(layout->widths, widths, items * sizeof(widths[0]));
- }
- layout->items = items;
- layout->position = mu_vec2(layout->indent, layout->next_row);
- layout->size.y = height;
- layout->item_index = 0;
-}
-
-
-void mu_layout_width(mu_Context *ctx, int width) {
- get_layout(ctx)->size.x = width;
-}
-
-
-void mu_layout_height(mu_Context *ctx, int height) {
- get_layout(ctx)->size.y = height;
-}
-
-
-void mu_layout_set_next(mu_Context *ctx, mu_Rect r, int relative) {
- mu_Layout *layout = get_layout(ctx);
- layout->next = r;
- layout->next_type = relative ? RELATIVE : ABSOLUTE;
-}
-
-
-mu_Rect mu_layout_next(mu_Context *ctx) {
- mu_Layout *layout = get_layout(ctx);
- mu_Style *style = ctx->style;
- mu_Rect res;
-
- if (layout->next_type) {
- /* handle rect set by `mu_layout_set_next` */
- int type = layout->next_type;
- layout->next_type = 0;
- res = layout->next;
- if (type == ABSOLUTE) { return (ctx->last_rect = res); }
-
- } else {
- /* handle next row */
- if (layout->item_index == layout->items) {
- mu_layout_row(ctx, layout->items, NULL, layout->size.y);
- }
-
- /* position */
- res.x = layout->position.x;
- res.y = layout->position.y;
-
- /* size */
- res.w = layout->items > 0 ? layout->widths[layout->item_index] : layout->size.x;
- res.h = layout->size.y;
- if (res.w == 0) { res.w = style->size.x + style->padding * 2; }
- if (res.h == 0) { res.h = style->size.y + style->padding * 2; }
- if (res.w < 0) { res.w += layout->body.w - res.x + 1; }
- if (res.h < 0) { res.h += layout->body.h - res.y + 1; }
-
- layout->item_index++;
- }
-
- /* update position */
- layout->position.x += res.w + style->spacing;
- layout->next_row = mu_max(layout->next_row, res.y + res.h + style->spacing);
-
- /* apply body offset */
- res.x += layout->body.x;
- res.y += layout->body.y;
-
- /* update max position */
- layout->max.x = mu_max(layout->max.x, res.x + res.w);
- layout->max.y = mu_max(layout->max.y, res.y + res.h);
-
- return (ctx->last_rect = res);
-}
-
-
-/*============================================================================
-** controls
-**============================================================================*/
-
-static int in_hover_root(mu_Context *ctx) {
- int i = ctx->container_stack.idx;
- while (i--) {
- if (ctx->container_stack.items[i] == ctx->hover_root) { return 1; }
- /* only root containers have their `head` field set; stop searching if we've
- ** reached the current root container */
- if (ctx->container_stack.items[i]->head) { break; }
- }
- return 0;
-}
-
-
-void mu_draw_control_frame(mu_Context *ctx, mu_Id id, mu_Rect rect,
- int colorid, int opt)
-{
- if (opt & MU_OPT_NOFRAME) { return; }
- colorid += (ctx->focus == id) ? 2 : (ctx->hover == id) ? 1 : 0;
- ctx->draw_frame(ctx, rect, colorid);
-}
-
-
-void mu_draw_control_text(mu_Context *ctx, const char *str, mu_Rect rect,
- int colorid, int opt)
-{
- mu_Vec2 pos;
- mu_Font font = ctx->style->font;
- int tw = ctx->text_width(font, str, -1);
- mu_push_clip_rect(ctx, rect);
- pos.y = rect.y + (rect.h - ctx->text_height(font)) / 2;
- if (opt & MU_OPT_ALIGNCENTER) {
- pos.x = rect.x + (rect.w - tw) / 2;
- } else if (opt & MU_OPT_ALIGNRIGHT) {
- pos.x = rect.x + rect.w - tw - ctx->style->padding;
- } else {
- pos.x = rect.x + ctx->style->padding;
- }
- mu_draw_text(ctx, font, str, -1, pos, ctx->style->colors[colorid]);
- mu_pop_clip_rect(ctx);
-}
-
-
-int mu_mouse_over(mu_Context *ctx, mu_Rect rect) {
- return rect_overlaps_vec2(rect, ctx->mouse_pos) &&
- rect_overlaps_vec2(mu_get_clip_rect(ctx), ctx->mouse_pos) &&
- in_hover_root(ctx);
-}
-
-
-void mu_update_control(mu_Context *ctx, mu_Id id, mu_Rect rect, int opt) {
- int mouseover = mu_mouse_over(ctx, rect);
-
- if (ctx->focus == id) { ctx->updated_focus = 1; }
- if (opt & MU_OPT_NOINTERACT) { return; }
- if (mouseover && !ctx->mouse_down) { ctx->hover = id; }
-
- if (ctx->focus == id) {
- if (ctx->mouse_pressed && !mouseover) { mu_set_focus(ctx, 0); }
- if (!ctx->mouse_down && ~opt & MU_OPT_HOLDFOCUS) { mu_set_focus(ctx, 0); }
- }
-
- if (ctx->hover == id) {
- if (ctx->mouse_pressed) {
- mu_set_focus(ctx, id);
- } else if (!mouseover) {
- ctx->hover = 0;
- }
- }
-}
-
-
-void mu_text(mu_Context *ctx, const char *text) {
- const char *start, *end, *p = text;
- int width = -1;
- mu_Font font = ctx->style->font;
- mu_Color color = ctx->style->colors[MU_COLOR_TEXT];
- mu_layout_begin_column(ctx);
- mu_layout_row(ctx, 1, &width, ctx->text_height(font));
- do {
- mu_Rect r = mu_layout_next(ctx);
- int w = 0;
- start = end = p;
- do {
- const char* word = p;
- while (*p && *p != ' ' && *p != '\n') { p++; }
- w += ctx->text_width(font, word, p - word);
- if (w > r.w && end != start) { break; }
- w += ctx->text_width(font, p, 1);
- end = p++;
- } while (*end && *end != '\n');
- mu_draw_text(ctx, font, start, end - start, mu_vec2(r.x, r.y), color);
- p = end + 1;
- } while (*end);
- mu_layout_end_column(ctx);
-}
-
-
-void mu_label(mu_Context *ctx, const char *text) {
- mu_draw_control_text(ctx, text, mu_layout_next(ctx), MU_COLOR_TEXT, 0);
-}
-
-
-int mu_button_ex(mu_Context *ctx, const char *label, int icon, int opt) {
- int res = 0;
- mu_Id id = label ? mu_get_id(ctx, label, strlen(label))
- : mu_get_id(ctx, &icon, sizeof(icon));
- mu_Rect r = mu_layout_next(ctx);
- mu_update_control(ctx, id, r, opt);
- /* handle click */
- if (ctx->mouse_pressed == MU_MOUSE_LEFT && ctx->focus == id) {
- res |= MU_RES_SUBMIT;
- }
- /* draw */
- mu_draw_control_frame(ctx, id, r, MU_COLOR_BUTTON, opt);
- if (label) { mu_draw_control_text(ctx, label, r, MU_COLOR_TEXT, opt); }
- if (icon) { mu_draw_icon(ctx, icon, r, ctx->style->colors[MU_COLOR_TEXT]); }
- return res;
-}
-
-
-int mu_checkbox(mu_Context *ctx, const char *label, int *state) {
- int res = 0;
- mu_Id id = mu_get_id(ctx, &state, sizeof(state));
- mu_Rect r = mu_layout_next(ctx);
- mu_Rect box = mu_rect(r.x, r.y, r.h, r.h);
- mu_update_control(ctx, id, r, 0);
- /* handle click */
- if (ctx->mouse_pressed == MU_MOUSE_LEFT && ctx->focus == id) {
- res |= MU_RES_CHANGE;
- *state = !*state;
- }
- /* draw */
- mu_draw_control_frame(ctx, id, box, MU_COLOR_BASE, 0);
- if (*state) {
- mu_draw_icon(ctx, MU_ICON_CHECK, box, ctx->style->colors[MU_COLOR_TEXT]);
- }
- r = mu_rect(r.x + box.w, r.y, r.w - box.w, r.h);
- mu_draw_control_text(ctx, label, r, MU_COLOR_TEXT, 0);
- return res;
-}
-
-
-int mu_textbox_raw(mu_Context *ctx, char *buf, int bufsz, mu_Id id, mu_Rect r,
- int opt)
-{
- int res = 0;
- mu_update_control(ctx, id, r, opt | MU_OPT_HOLDFOCUS);
-
- if (ctx->focus == id) {
- /* handle text input */
- int len = strlen(buf);
- int n = mu_min(bufsz - len - 1, (int) strlen(ctx->input_text));
- if (n > 0) {
- memcpy(buf + len, ctx->input_text, n);
- len += n;
- buf[len] = '\0';
- res |= MU_RES_CHANGE;
- }
- /* handle backspace */
- if (ctx->key_pressed & MU_KEY_BACKSPACE && len > 0) {
- /* skip utf-8 continuation bytes */
- while ((buf[--len] & 0xc0) == 0x80 && len > 0);
- buf[len] = '\0';
- res |= MU_RES_CHANGE;
- }
- /* handle return */
- if (ctx->key_pressed & MU_KEY_RETURN) {
- mu_set_focus(ctx, 0);
- res |= MU_RES_SUBMIT;
- }
- }
-
- /* draw */
- mu_draw_control_frame(ctx, id, r, MU_COLOR_BASE, opt);
- if (ctx->focus == id) {
- mu_Color color = ctx->style->colors[MU_COLOR_TEXT];
- mu_Font font = ctx->style->font;
- int textw = ctx->text_width(font, buf, -1);
- int texth = ctx->text_height(font);
- int ofx = r.w - ctx->style->padding - textw - 1;
- int textx = r.x + mu_min(ofx, ctx->style->padding);
- int texty = r.y + (r.h - texth) / 2;
- mu_push_clip_rect(ctx, r);
- mu_draw_text(ctx, font, buf, -1, mu_vec2(textx, texty), color);
- mu_draw_rect(ctx, mu_rect(textx + textw, texty, 1, texth), color);
- mu_pop_clip_rect(ctx);
- } else {
- mu_draw_control_text(ctx, buf, r, MU_COLOR_TEXT, opt);
- }
-
- return res;
-}
-
-
-static int number_textbox(mu_Context *ctx, mu_Real *value, mu_Rect r, mu_Id id) {
- if (ctx->mouse_pressed == MU_MOUSE_LEFT && ctx->key_down & MU_KEY_SHIFT &&
- ctx->hover == id
- ) {
- ctx->number_edit = id;
- sprintf(ctx->number_edit_buf, MU_REAL_FMT, *value);
- }
- if (ctx->number_edit == id) {
- int res = mu_textbox_raw(
- ctx, ctx->number_edit_buf, sizeof(ctx->number_edit_buf), id, r, 0);
- if (res & MU_RES_SUBMIT || ctx->focus != id) {
- *value = strtod(ctx->number_edit_buf, NULL);
- ctx->number_edit = 0;
- } else {
- return 1;
- }
- }
- return 0;
-}
-
-
-int mu_textbox_ex(mu_Context *ctx, char *buf, int bufsz, int opt) {
- mu_Id id = mu_get_id(ctx, &buf, sizeof(buf));
- mu_Rect r = mu_layout_next(ctx);
- return mu_textbox_raw(ctx, buf, bufsz, id, r, opt);
-}
-
-
-int mu_slider_ex(mu_Context *ctx, mu_Real *value, mu_Real low, mu_Real high,
- mu_Real step, const char *fmt, int opt)
-{
- char buf[MU_MAX_FMT + 1];
- mu_Rect thumb;
- int x, w, res = 0;
- mu_Real last = *value, v = last;
- mu_Id id = mu_get_id(ctx, &value, sizeof(value));
- mu_Rect base = mu_layout_next(ctx);
-
- /* handle text input mode */
- if (number_textbox(ctx, &v, base, id)) { return res; }
-
- /* handle normal mode */
- mu_update_control(ctx, id, base, opt);
-
- /* handle input */
- if (ctx->focus == id &&
- (ctx->mouse_down | ctx->mouse_pressed) == MU_MOUSE_LEFT)
- {
- v = low + (ctx->mouse_pos.x - base.x) * (high - low) / base.w;
- if (step) { v = (((v + step / 2) / step)) * step; }
- }
- /* clamp and store value, update res */
- *value = v = mu_clamp(v, low, high);
- if (last != v) { res |= MU_RES_CHANGE; }
-
- /* draw base */
- mu_draw_control_frame(ctx, id, base, MU_COLOR_BASE, opt);
- /* draw thumb */
- w = ctx->style->thumb_size;
- x = (v - low) * (base.w - w) / (high - low);
- thumb = mu_rect(base.x + x, base.y, w, base.h);
- mu_draw_control_frame(ctx, id, thumb, MU_COLOR_BUTTON, opt);
- /* draw text */
- sprintf(buf, fmt, v);
- mu_draw_control_text(ctx, buf, base, MU_COLOR_TEXT, opt);
-
- return res;
-}
-
-
-int mu_number_ex(mu_Context *ctx, mu_Real *value, mu_Real step,
- const char *fmt, int opt)
-{
- char buf[MU_MAX_FMT + 1];
- int res = 0;
- mu_Id id = mu_get_id(ctx, &value, sizeof(value));
- mu_Rect base = mu_layout_next(ctx);
- mu_Real last = *value;
-
- /* handle text input mode */
- if (number_textbox(ctx, value, base, id)) { return res; }
-
- /* handle normal mode */
- mu_update_control(ctx, id, base, opt);
-
- /* handle input */
- if (ctx->focus == id && ctx->mouse_down == MU_MOUSE_LEFT) {
- *value += ctx->mouse_delta.x * step;
- }
- /* set flag if value changed */
- if (*value != last) { res |= MU_RES_CHANGE; }
-
- /* draw base */
- mu_draw_control_frame(ctx, id, base, MU_COLOR_BASE, opt);
- /* draw text */
- sprintf(buf, fmt, *value);
- mu_draw_control_text(ctx, buf, base, MU_COLOR_TEXT, opt);
-
- return res;
-}
-
-
-static int header(mu_Context *ctx, const char *label, int istreenode, int opt) {
- mu_Rect r;
- int active, expanded;
- mu_Id id = mu_get_id(ctx, label, strlen(label));
- int idx = mu_pool_get(ctx, ctx->treenode_pool, MU_TREENODEPOOL_SIZE, id);
- int width = -1;
- mu_layout_row(ctx, 1, &width, 0);
-
- active = (idx >= 0);
- expanded = (opt & MU_OPT_EXPANDED) ? !active : active;
- r = mu_layout_next(ctx);
- mu_update_control(ctx, id, r, 0);
-
- /* handle click */
- active ^= (ctx->mouse_pressed == MU_MOUSE_LEFT && ctx->focus == id);
-
- /* update pool ref */
- if (idx >= 0) {
- if (active) { mu_pool_update(ctx, ctx->treenode_pool, idx); }
- else { memset(&ctx->treenode_pool[idx], 0, sizeof(mu_PoolItem)); }
- } else if (active) {
- mu_pool_init(ctx, ctx->treenode_pool, MU_TREENODEPOOL_SIZE, id);
- }
-
- /* draw */
- if (istreenode) {
- if (ctx->hover == id) { ctx->draw_frame(ctx, r, MU_COLOR_BUTTONHOVER); }
- } else {
- mu_draw_control_frame(ctx, id, r, MU_COLOR_BUTTON, 0);
- }
- mu_draw_icon(
- ctx, expanded ? MU_ICON_EXPANDED : MU_ICON_COLLAPSED,
- mu_rect(r.x, r.y, r.h, r.h), ctx->style->colors[MU_COLOR_TEXT]);
- r.x += r.h - ctx->style->padding;
- r.w -= r.h - ctx->style->padding;
- mu_draw_control_text(ctx, label, r, MU_COLOR_TEXT, 0);
-
- return expanded ? MU_RES_ACTIVE : 0;
-}
-
-
-int mu_header_ex(mu_Context *ctx, const char *label, int opt) {
- return header(ctx, label, 0, opt);
-}
-
-
-int mu_begin_treenode_ex(mu_Context *ctx, const char *label, int opt) {
- int res = header(ctx, label, 1, opt);
- if (res & MU_RES_ACTIVE) {
- get_layout(ctx)->indent += ctx->style->indent;
- push(ctx->id_stack, ctx->last_id);
- }
- return res;
-}
-
-
-void mu_end_treenode(mu_Context *ctx) {
- get_layout(ctx)->indent -= ctx->style->indent;
- mu_pop_id(ctx);
-}
-
-
-#define scrollbar(ctx, cnt, b, cs, x, y, w, h) \
- do { \
- /* only add scrollbar if content size is larger than body */ \
- int maxscroll = cs.y - b->h; \
- \
- if (maxscroll > 0 && b->h > 0) { \
- mu_Rect base, thumb; \
- mu_Id id = mu_get_id(ctx, "!scrollbar" #y, 11); \
- \
- /* get sizing / positioning */ \
- base = *b; \
- base.x = b->x + b->w; \
- base.w = ctx->style->scrollbar_size; \
- \
- /* handle input */ \
- mu_update_control(ctx, id, base, 0); \
- if (ctx->focus == id && ctx->mouse_down == MU_MOUSE_LEFT) { \
- cnt->scroll.y += ctx->mouse_delta.y * cs.y / base.h; \
- } \
- /* clamp scroll to limits */ \
- cnt->scroll.y = mu_clamp(cnt->scroll.y, 0, maxscroll); \
- \
- /* draw base and thumb */ \
- ctx->draw_frame(ctx, base, MU_COLOR_SCROLLBASE); \
- thumb = base; \
- thumb.h = mu_max(ctx->style->thumb_size, base.h * b->h / cs.y); \
- thumb.y += cnt->scroll.y * (base.h - thumb.h) / maxscroll; \
- ctx->draw_frame(ctx, thumb, MU_COLOR_SCROLLTHUMB); \
- \
- /* set this as the scroll_target (will get scrolled on mousewheel) */ \
- /* if the mouse is over it */ \
- if (mu_mouse_over(ctx, *b)) { ctx->scroll_target = cnt; } \
- } else { \
- cnt->scroll.y = 0; \
- } \
- } while (0)
-
-
-static void scrollbars(mu_Context *ctx, mu_Container *cnt, mu_Rect *body) {
- int sz = ctx->style->scrollbar_size;
- mu_Vec2 cs = cnt->content_size;
- cs.x += ctx->style->padding * 2;
- cs.y += ctx->style->padding * 2;
- mu_push_clip_rect(ctx, *body);
- /* resize body to make room for scrollbars */
- if (cs.y > cnt->body.h) { body->w -= sz; }
- if (cs.x > cnt->body.w) { body->h -= sz; }
- /* to create a horizontal or vertical scrollbar almost-identical code is
- ** used; only the references to `x|y` `w|h` need to be switched */
- scrollbar(ctx, cnt, body, cs, x, y, w, h);
- scrollbar(ctx, cnt, body, cs, y, x, h, w);
- mu_pop_clip_rect(ctx);
-}
-
-
-static void push_container_body(
- mu_Context *ctx, mu_Container *cnt, mu_Rect body, int opt
-) {
- if (~opt & MU_OPT_NOSCROLL) { scrollbars(ctx, cnt, &body); }
- push_layout(ctx, expand_rect(body, -ctx->style->padding), cnt->scroll);
- cnt->body = body;
-}
-
-
-static void begin_root_container(mu_Context *ctx, mu_Container *cnt) {
- push(ctx->container_stack, cnt);
- /* push container to roots list and push head command */
- push(ctx->root_list, cnt);
- cnt->head = push_jump(ctx, NULL);
- /* set as hover root if the mouse is overlapping this container and it has a
- ** higher zindex than the current hover root */
- if (rect_overlaps_vec2(cnt->rect, ctx->mouse_pos) &&
- (!ctx->next_hover_root || cnt->zindex > ctx->next_hover_root->zindex)
- ) {
- ctx->next_hover_root = cnt;
- }
- /* clipping is reset here in case a root-container is made within
- ** another root-containers's begin/end block; this prevents the inner
- ** root-container being clipped to the outer */
- push(ctx->clip_stack, unclipped_rect);
-}
-
-
-static void end_root_container(mu_Context *ctx) {
- /* push tail 'goto' jump command and set head 'skip' command. the final steps
- ** on initing these are done in mu_end() */
- mu_Container *cnt = mu_get_current_container(ctx);
- cnt->tail = push_jump(ctx, NULL);
- cnt->head->jump.dst = ctx->command_list.items + ctx->command_list.idx;
- /* pop base clip rect and container */
- mu_pop_clip_rect(ctx);
- pop_container(ctx);
-}
-
-
-int mu_begin_window_ex(mu_Context *ctx, const char *title, mu_Rect rect, int opt) {
- mu_Rect body;
- mu_Id id = mu_get_id(ctx, title, strlen(title));
- mu_Container *cnt = get_container(ctx, id, opt);
- if (!cnt || !cnt->open) { return 0; }
- push(ctx->id_stack, id);
-
- if (cnt->rect.w == 0) { cnt->rect = rect; }
- begin_root_container(ctx, cnt);
- rect = body = cnt->rect;
-
- /* draw frame */
- if (~opt & MU_OPT_NOFRAME) {
- ctx->draw_frame(ctx, rect, MU_COLOR_WINDOWBG);
- }
-
- /* do title bar */
- if (~opt & MU_OPT_NOTITLE) {
- mu_Rect tr = rect;
- tr.h = ctx->style->title_height;
- ctx->draw_frame(ctx, tr, MU_COLOR_TITLEBG);
-
- /* do title text */
- if (~opt & MU_OPT_NOTITLE) {
- mu_Id id = mu_get_id(ctx, "!title", 6);
- mu_update_control(ctx, id, tr, opt);
- mu_draw_control_text(ctx, title, tr, MU_COLOR_TITLETEXT, opt);
- if (id == ctx->focus && ctx->mouse_down == MU_MOUSE_LEFT) {
- cnt->rect.x += ctx->mouse_delta.x;
- cnt->rect.y += ctx->mouse_delta.y;
- }
- body.y += tr.h;
- body.h -= tr.h;
- }
-
- /* do `close` button */
- if (~opt & MU_OPT_NOCLOSE) {
- mu_Id id = mu_get_id(ctx, "!close", 6);
- mu_Rect r = mu_rect(tr.x + tr.w - tr.h, tr.y, tr.h, tr.h);
- tr.w -= r.w;
- mu_draw_icon(ctx, MU_ICON_CLOSE, r, ctx->style->colors[MU_COLOR_TITLETEXT]);
- mu_update_control(ctx, id, r, opt);
- if (ctx->mouse_pressed == MU_MOUSE_LEFT && id == ctx->focus) {
- cnt->open = 0;
- }
- }
- }
-
- push_container_body(ctx, cnt, body, opt);
-
- /* do `resize` handle */
- if (~opt & MU_OPT_NORESIZE) {
- int sz = ctx->style->title_height;
- mu_Id id = mu_get_id(ctx, "!resize", 7);
- mu_Rect r = mu_rect(rect.x + rect.w - sz, rect.y + rect.h - sz, sz, sz);
- mu_update_control(ctx, id, r, opt);
- if (id == ctx->focus && ctx->mouse_down == MU_MOUSE_LEFT) {
- cnt->rect.w = mu_max(96, cnt->rect.w + ctx->mouse_delta.x);
- cnt->rect.h = mu_max(64, cnt->rect.h + ctx->mouse_delta.y);
- }
- }
-
- /* resize to content size */
- if (opt & MU_OPT_AUTOSIZE) {
- mu_Rect r = get_layout(ctx)->body;
- cnt->rect.w = cnt->content_size.x + (cnt->rect.w - r.w);
- cnt->rect.h = cnt->content_size.y + (cnt->rect.h - r.h);
- }
-
- /* close if this is a popup window and elsewhere was clicked */
- if (opt & MU_OPT_POPUP && ctx->mouse_pressed && ctx->hover_root != cnt) {
- cnt->open = 0;
- }
-
- mu_push_clip_rect(ctx, cnt->body);
- return MU_RES_ACTIVE;
-}
-
-
-void mu_end_window(mu_Context *ctx) {
- mu_pop_clip_rect(ctx);
- end_root_container(ctx);
-}
-
-
-void mu_open_popup(mu_Context *ctx, const char *name) {
- mu_Container *cnt = mu_get_container(ctx, name);
- /* set as hover root so popup isn't closed in begin_window_ex() */
- ctx->hover_root = ctx->next_hover_root = cnt;
- /* position at mouse cursor, open and bring-to-front */
- cnt->rect = mu_rect(ctx->mouse_pos.x, ctx->mouse_pos.y, 1, 1);
- cnt->open = 1;
- mu_bring_to_front(ctx, cnt);
-}
-
-
-int mu_begin_popup(mu_Context *ctx, const char *name) {
- int opt = MU_OPT_POPUP | MU_OPT_AUTOSIZE | MU_OPT_NORESIZE |
- MU_OPT_NOSCROLL | MU_OPT_NOTITLE | MU_OPT_CLOSED;
- return mu_begin_window_ex(ctx, name, mu_rect(0, 0, 0, 0), opt);
-}
-
-
-void mu_end_popup(mu_Context *ctx) {
- mu_end_window(ctx);
-}
-
-
-void mu_begin_panel_ex(mu_Context *ctx, const char *name, int opt) {
- mu_Container *cnt;
- mu_push_id(ctx, name, strlen(name));
- cnt = get_container(ctx, ctx->last_id, opt);
- cnt->rect = mu_layout_next(ctx);
- if (~opt & MU_OPT_NOFRAME) {
- ctx->draw_frame(ctx, cnt->rect, MU_COLOR_PANELBG);
- }
- push(ctx->container_stack, cnt);
- push_container_body(ctx, cnt, cnt->rect, opt);
- mu_push_clip_rect(ctx, cnt->body);
-}
-
-
-void mu_end_panel(mu_Context *ctx) {
- mu_pop_clip_rect(ctx);
- pop_container(ctx);
-}
diff --git a/microui.h b/microui.h
deleted file mode 100644
index 78a1a341fe93..000000000000
--- a/microui.h
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
-** Copyright (c) 2020 rxi
-**
-** This library is free software; you can redistribute it and/or modify it
-** under the terms of the MIT license. See `microui.c` for details.
-*/
-
-#ifndef MICROUI_H
-#define MICROUI_H
-
-#define MU_VERSION "2.01"
-
-#define MU_COMMANDLIST_SIZE (256 * 1024)
-#define MU_ROOTLIST_SIZE 32
-#define MU_CONTAINERSTACK_SIZE 32
-#define MU_CLIPSTACK_SIZE 32
-#define MU_IDSTACK_SIZE 32
-#define MU_LAYOUTSTACK_SIZE 16
-#define MU_CONTAINERPOOL_SIZE 48
-#define MU_TREENODEPOOL_SIZE 48
-#define MU_MAX_WIDTHS 16
-#define MU_REAL float
-#define MU_REAL_FMT "%.3g"
-#define MU_SLIDER_FMT "%.2f"
-#define MU_MAX_FMT 127
-
-#define mu_stack(T, n) struct { int idx; T items[n]; }
-#define mu_min(a, b) ((a) < (b) ? (a) : (b))
-#define mu_max(a, b) ((a) > (b) ? (a) : (b))
-#define mu_clamp(x, a, b) mu_min(b, mu_max(a, x))
-
-enum {
- MU_CLIP_PART = 1,
- MU_CLIP_ALL
-};
-
-enum {
- MU_COMMAND_JUMP = 1,
- MU_COMMAND_CLIP,
- MU_COMMAND_RECT,
- MU_COMMAND_TEXT,
- MU_COMMAND_ICON,
- MU_COMMAND_MAX
-};
-
-enum {
- MU_COLOR_TEXT,
- MU_COLOR_BORDER,
- MU_COLOR_WINDOWBG,
- MU_COLOR_TITLEBG,
- MU_COLOR_TITLETEXT,
- MU_COLOR_PANELBG,
- MU_COLOR_BUTTON,
- MU_COLOR_BUTTONHOVER,
- MU_COLOR_BUTTONFOCUS,
- MU_COLOR_BASE,
- MU_COLOR_BASEHOVER,
- MU_COLOR_BASEFOCUS,
- MU_COLOR_SCROLLBASE,
- MU_COLOR_SCROLLTHUMB,
- MU_COLOR_MAX
-};
-
-enum {
- MU_ICON_CLOSE = 1,
- MU_ICON_CHECK,
- MU_ICON_COLLAPSED,
- MU_ICON_EXPANDED,
- MU_ICON_MAX
-};
-
-enum {
- MU_RES_ACTIVE = (1 << 0),
- MU_RES_SUBMIT = (1 << 1),
- MU_RES_CHANGE = (1 << 2)
-};
-
-enum {
- MU_OPT_ALIGNCENTER = (1 << 0),
- MU_OPT_ALIGNRIGHT = (1 << 1),
- MU_OPT_NOINTERACT = (1 << 2),
- MU_OPT_NOFRAME = (1 << 3),
- MU_OPT_NORESIZE = (1 << 4),
- MU_OPT_NOSCROLL = (1 << 5),
- MU_OPT_NOCLOSE = (1 << 6),
- MU_OPT_NOTITLE = (1 << 7),
- MU_OPT_HOLDFOCUS = (1 << 8),
- MU_OPT_AUTOSIZE = (1 << 9),
- MU_OPT_POPUP = (1 << 10),
- MU_OPT_CLOSED = (1 << 11),
- MU_OPT_EXPANDED = (1 << 12)
-};
-
-enum {
- MU_MOUSE_LEFT = (1 << 0),
- MU_MOUSE_RIGHT = (1 << 1),
- MU_MOUSE_MIDDLE = (1 << 2)
-};
-
-enum {
- MU_KEY_SHIFT = (1 << 0),
- MU_KEY_CTRL = (1 << 1),
- MU_KEY_ALT = (1 << 2),
- MU_KEY_BACKSPACE = (1 << 3),
- MU_KEY_RETURN = (1 << 4)
-};
-
-
-typedef struct mu_Context mu_Context;
-typedef unsigned mu_Id;
-typedef MU_REAL mu_Real;
-typedef void* mu_Font;
-
-typedef struct { int x, y; } mu_Vec2;
-typedef struct { int x, y, w, h; } mu_Rect;
-typedef struct { unsigned char r, g, b, a; } mu_Color;
-typedef struct { mu_Id id; int last_update; } mu_PoolItem;
-
-typedef struct { int type, size; } mu_BaseCommand;
-typedef struct { mu_BaseCommand base; void *dst; } mu_JumpCommand;
-typedef struct { mu_BaseCommand base; mu_Rect rect; } mu_ClipCommand;
-typedef struct { mu_BaseCommand base; mu_Rect rect; mu_Color color; } mu_RectCommand;
-typedef struct { mu_BaseCommand base; mu_Font font; mu_Vec2 pos; mu_Color color; char str[1]; } mu_TextCommand;
-typedef struct { mu_BaseCommand base; mu_Rect rect; int id; mu_Color color; } mu_IconCommand;
-
-typedef union {
- int type;
- mu_BaseCommand base;
- mu_JumpCommand jump;
- mu_ClipCommand clip;
- mu_RectCommand rect;
- mu_TextCommand text;
- mu_IconCommand icon;
-} mu_Command;
-
-typedef struct {
- mu_Rect body;
- mu_Rect next;
- mu_Vec2 position;
- mu_Vec2 size;
- mu_Vec2 max;
- int widths[MU_MAX_WIDTHS];
- int items;
- int item_index;
- int next_row;
- int next_type;
- int indent;
-} mu_Layout;
-
-typedef struct {
- mu_Command *head, *tail;
- mu_Rect rect;
- mu_Rect body;
- mu_Vec2 content_size;
- mu_Vec2 scroll;
- int zindex;
- int open;
-} mu_Container;
-
-typedef struct {
- mu_Font font;
- mu_Vec2 size;
- int padding;
- int spacing;
- int indent;
- int title_height;
- int scrollbar_size;
- int thumb_size;
- mu_Color colors[MU_COLOR_MAX];
-} mu_Style;
-
-struct mu_Context {
- /* callbacks */
- int (*text_width)(mu_Font font, const char *str, int len);
- int (*text_height)(mu_Font font);
- void (*draw_frame)(mu_Context *ctx, mu_Rect rect, int colorid);
- /* core state */
- mu_Style _style;
- mu_Style *style;
- mu_Id hover;
- mu_Id focus;
- mu_Id last_id;
- mu_Rect last_rect;
- int last_zindex;
- int updated_focus;
- int frame;
- mu_Container *hover_root;
- mu_Container *next_hover_root;
- mu_Container *scroll_target;
- char number_edit_buf[MU_MAX_FMT];
- mu_Id number_edit;
- /* stacks */
- mu_stack(char, MU_COMMANDLIST_SIZE) command_list;
- mu_stack(mu_Container*, MU_ROOTLIST_SIZE) root_list;
- mu_stack(mu_Container*, MU_CONTAINERSTACK_SIZE) container_stack;
- mu_stack(mu_Rect, MU_CLIPSTACK_SIZE) clip_stack;
- mu_stack(mu_Id, MU_IDSTACK_SIZE) id_stack;
- mu_stack(mu_Layout, MU_LAYOUTSTACK_SIZE) layout_stack;
- /* retained state pools */
- mu_PoolItem container_pool[MU_CONTAINERPOOL_SIZE];
- mu_Container containers[MU_CONTAINERPOOL_SIZE];
- mu_PoolItem treenode_pool[MU_TREENODEPOOL_SIZE];
- /* input state */
- mu_Vec2 mouse_pos;
- mu_Vec2 last_mouse_pos;
- mu_Vec2 mouse_delta;
- mu_Vec2 scroll_delta;
- int mouse_down;
- int mouse_pressed;
- int key_down;
- int key_pressed;
- char input_text[32];
-};
-
-
-mu_Vec2 mu_vec2(int x, int y);
-mu_Rect mu_rect(int x, int y, int w, int h);
-mu_Color mu_color(int r, int g, int b, int a);
-
-void mu_init(mu_Context *ctx);
-void mu_begin(mu_Context *ctx);
-void mu_end(mu_Context *ctx);
-void mu_set_focus(mu_Context *ctx, mu_Id id);
-mu_Id mu_get_id(mu_Context *ctx, const void *data, int size);
-void mu_push_id(mu_Context *ctx, const void *data, int size);
-void mu_pop_id(mu_Context *ctx);
-void mu_push_clip_rect(mu_Context *ctx, mu_Rect rect);
-void mu_pop_clip_rect(mu_Context *ctx);
-mu_Rect mu_get_clip_rect(mu_Context *ctx);
-int mu_check_clip(mu_Context *ctx, mu_Rect r);
-mu_Container* mu_get_current_container(mu_Context *ctx);
-mu_Container* mu_get_container(mu_Context *ctx, const char *name);
-void mu_bring_to_front(mu_Context *ctx, mu_Container *cnt);
-
-int mu_pool_init(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id);
-int mu_pool_get(mu_Context *ctx, mu_PoolItem *items, int len, mu_Id id);
-void mu_pool_update(mu_Context *ctx, mu_PoolItem *items, int idx);
-
-void mu_input_mousemove(mu_Context *ctx, int x, int y);
-void mu_input_mousedown(mu_Context *ctx, int x, int y, int btn);
-void mu_input_mouseup(mu_Context *ctx, int x, int y, int btn);
-void mu_input_scroll(mu_Context *ctx, int x, int y);
-void mu_input_keydown(mu_Context *ctx, int key);
-void mu_input_keyup(mu_Context *ctx, int key);
-void mu_input_text(mu_Context *ctx, const char *text);
-
-mu_Command* mu_push_command(mu_Context *ctx, int type, int size);
-int mu_next_command(mu_Context *ctx, mu_Command **cmd);
-void mu_set_clip(mu_Context *ctx, mu_Rect rect);
-void mu_draw_rect(mu_Context *ctx, mu_Rect rect, mu_Color color);
-void mu_draw_box(mu_Context *ctx, mu_Rect rect, mu_Color color);
-void mu_draw_text(mu_Context *ctx, mu_Font font, const char *str, int len, mu_Vec2 pos, mu_Color color);
-void mu_draw_icon(mu_Context *ctx, int id, mu_Rect rect, mu_Color color);
-
-void mu_layout_row(mu_Context *ctx, int items, const int *widths, int height);
-void mu_layout_width(mu_Context *ctx, int width);
-void mu_layout_height(mu_Context *ctx, int height);
-void mu_layout_begin_column(mu_Context *ctx);
-void mu_layout_end_column(mu_Context *ctx);
-void mu_layout_set_next(mu_Context *ctx, mu_Rect r, int relative);
-mu_Rect mu_layout_next(mu_Context *ctx);
-
-void mu_draw_control_frame(mu_Context *ctx, mu_Id id, mu_Rect rect, int colorid, int opt);
-void mu_draw_control_text(mu_Context *ctx, const char *str, mu_Rect rect, int colorid, int opt);
-int mu_mouse_over(mu_Context *ctx, mu_Rect rect);
-void mu_update_control(mu_Context *ctx, mu_Id id, mu_Rect rect, int opt);
-
-#define mu_button(ctx, label) mu_button_ex(ctx, label, 0, MU_OPT_ALIGNCENTER)
-#define mu_textbox(ctx, buf, bufsz) mu_textbox_ex(ctx, buf, bufsz, 0)
-#define mu_slider(ctx, value, lo, hi) mu_slider_ex(ctx, value, lo, hi, 0, MU_SLIDER_FMT, MU_OPT_ALIGNCENTER)
-#define mu_number(ctx, value, step) mu_number_ex(ctx, value, step, MU_SLIDER_FMT, MU_OPT_ALIGNCENTER)
-#define mu_header(ctx, label) mu_header_ex(ctx, label, 0)
-#define mu_begin_treenode(ctx, label) mu_begin_treenode_ex(ctx, label, 0)
-#define mu_begin_window(ctx, title, rect) mu_begin_window_ex(ctx, title, rect, 0)
-#define mu_begin_panel(ctx, name) mu_begin_panel_ex(ctx, name, 0)
-
-void mu_text(mu_Context *ctx, const char *text);
-void mu_label(mu_Context *ctx, const char *text);
-int mu_button_ex(mu_Context *ctx, const char *label, int icon, int opt);
-int mu_checkbox(mu_Context *ctx, const char *label, int *state);
-int mu_textbox_raw(mu_Context *ctx, char *buf, int bufsz, mu_Id id, mu_Rect r, int opt);
-int mu_textbox_ex(mu_Context *ctx, char *buf, int bufsz, int opt);
-int mu_slider_ex(mu_Context *ctx, mu_Real *value, mu_Real low, mu_Real high, mu_Real step, const char *fmt, int opt);
-int mu_number_ex(mu_Context *ctx, mu_Real *value, mu_Real step, const char *fmt, int opt);
-int mu_header_ex(mu_Context *ctx, const char *label, int opt);
-int mu_begin_treenode_ex(mu_Context *ctx, const char *label, int opt);
-void mu_end_treenode(mu_Context *ctx);
-int mu_begin_window_ex(mu_Context *ctx, const char *title, mu_Rect rect, int opt);
-void mu_end_window(mu_Context *ctx);
-void mu_open_popup(mu_Context *ctx, const char *name);
-int mu_begin_popup(mu_Context *ctx, const char *name);
-void mu_end_popup(mu_Context *ctx);
-void mu_begin_panel_ex(mu_Context *ctx, const char *name, int opt);
-void mu_end_panel(mu_Context *ctx);
-
-#endif
diff --git a/parson.c b/parson.c
deleted file mode 100644
index 4a18fe9a487c..000000000000
--- a/parson.c
+++ /dev/null
@@ -1,2465 +0,0 @@
-/*
- SPDX-License-Identifier: MIT
-
- Parson 1.5.2 (https://github.com/kgabis/parson)
- Copyright (c) 2012 - 2023 Krzysztof Gabis
-
- 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.
-*/
-#ifdef _MSC_VER
-#ifndef _CRT_SECURE_NO_WARNINGS
-#define _CRT_SECURE_NO_WARNINGS
-#endif /* _CRT_SECURE_NO_WARNINGS */
-#endif /* _MSC_VER */
-
-#include "parson.h"
-
-#define PARSON_IMPL_VERSION_MAJOR 1
-#define PARSON_IMPL_VERSION_MINOR 5
-#define PARSON_IMPL_VERSION_PATCH 2
-
-#if (PARSON_VERSION_MAJOR != PARSON_IMPL_VERSION_MAJOR)\
-|| (PARSON_VERSION_MINOR != PARSON_IMPL_VERSION_MINOR)\
-|| (PARSON_VERSION_PATCH != PARSON_IMPL_VERSION_PATCH)
-#error "parson version mismatch between parson.c and parson.h"
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-#include <errno.h>
-
-/* Apparently sscanf is not implemented in some "standard" libraries, so don't use it, if you
- * don't have to. */
-#ifdef sscanf
-#undef sscanf
-#define sscanf THINK_TWICE_ABOUT_USING_SSCANF
-#endif
-
-/* strcpy is unsafe */
-#ifdef strcpy
-#undef strcpy
-#endif
-#define strcpy USE_MEMCPY_INSTEAD_OF_STRCPY
-
-#define STARTING_CAPACITY 16
-#define MAX_NESTING 2048
-
-#ifndef PARSON_DEFAULT_FLOAT_FORMAT
-#define PARSON_DEFAULT_FLOAT_FORMAT "%1.17g" /* do not increase precision without incresing NUM_BUF_SIZE */
-#endif
-
-#ifndef PARSON_NUM_BUF_SIZE
-#define PARSON_NUM_BUF_SIZE 64 /* double printed with "%1.17g" shouldn't be longer than 25 bytes so let's be paranoid and use 64 */
-#endif
-
-#ifndef PARSON_INDENT_STR
-#define PARSON_INDENT_STR " "
-#endif
-
-#define SIZEOF_TOKEN(a) (sizeof(a) - 1)
-#define SKIP_CHAR(str) ((*str)++)
-#define SKIP_WHITESPACES(str) while (isspace((unsigned char)(**str))) { SKIP_CHAR(str); }
-#define MAX(a, b) ((a) > (b) ? (a) : (b))
-
-#undef malloc
-#undef free
-
-#if defined(isnan) && defined(isinf)
-#define IS_NUMBER_INVALID(x) (isnan((x)) || isinf((x)))
-#else
-#define IS_NUMBER_INVALID(x) (((x) * 0.0) != 0.0)
-#endif
-
-#define OBJECT_INVALID_IX ((size_t)-1)
-
-static JSON_Malloc_Function parson_malloc = malloc;
-static JSON_Free_Function parson_free = free;
-
-static int parson_escape_slashes = 1;
-
-static char *parson_float_format = NULL;
-
-static JSON_Number_Serialization_Function parson_number_serialization_function = NULL;
-
-#define IS_CONT(b) (((unsigned char)(b) & 0xC0) == 0x80) /* is utf-8 continuation byte */
-
-typedef int parson_bool_t;
-
-#define PARSON_TRUE 1
-#define PARSON_FALSE 0
-
-typedef struct json_string {
- char *chars;
- size_t length;
-} JSON_String;
-
-/* Type definitions */
-typedef union json_value_value {
- JSON_String string;
- double number;
- JSON_Object *object;
- JSON_Array *array;
- int boolean;
- int null;
-} JSON_Value_Value;
-
-struct json_value_t {
- JSON_Value *parent;
- JSON_Value_Type type;
- JSON_Value_Value value;
-};
-
-struct json_object_t {
- JSON_Value *wrapping_value;
- size_t *cells;
- unsigned long *hashes;
- char **names;
- JSON_Value **values;
- size_t *cell_ixs;
- size_t count;
- size_t item_capacity;
- size_t cell_capacity;
-};
-
-struct json_array_t {
- JSON_Value *wrapping_value;
- JSON_Value **items;
- size_t count;
- size_t capacity;
-};
-
-/* Various */
-static char * read_file(const char *filename);
-static void remove_comments(char *string, const char *start_token, const char *end_token);
-static char * parson_strndup(const char *string, size_t n);
-static char * parson_strdup(const char *string);
-static int hex_char_to_int(char c);
-static JSON_Status parse_utf16_hex(const char *string, unsigned int *result);
-static int num_bytes_in_utf8_sequence(unsigned char c);
-static JSON_Status verify_utf8_sequence(const unsigned char *string, int *len);
-static parson_bool_t is_valid_utf8(const char *string, size_t string_len);
-static parson_bool_t is_decimal(const char *string, size_t length);
-static unsigned long hash_string(const char *string, size_t n);
-
-/* JSON Object */
-static JSON_Object * json_object_make(JSON_Value *wrapping_value);
-static JSON_Status json_object_init(JSON_Object *object, size_t capacity);
-static void json_object_deinit(JSON_Object *object, parson_bool_t free_keys, parson_bool_t free_values);
-static JSON_Status json_object_grow_and_rehash(JSON_Object *object);
-static size_t json_object_get_cell_ix(const JSON_Object *object, const char *key, size_t key_len, unsigned long hash, parson_bool_t *out_found);
-static JSON_Status json_object_add(JSON_Object *object, char *name, JSON_Value *value);
-static JSON_Value * json_object_getn_value(const JSON_Object *object, const char *name, size_t name_len);
-static JSON_Status json_object_remove_internal(JSON_Object *object, const char *name, parson_bool_t free_value);
-static JSON_Status json_object_dotremove_internal(JSON_Object *object, const char *name, parson_bool_t free_value);
-static void json_object_free(JSON_Object *object);
-
-/* JSON Array */
-static JSON_Array * json_array_make(JSON_Value *wrapping_value);
-static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value);
-static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity);
-static void json_array_free(JSON_Array *array);
-
-/* JSON Value */
-static JSON_Value * json_value_init_string_no_copy(char *string, size_t length);
-static const JSON_String * json_value_get_string_desc(const JSON_Value *value);
-
-/* Parser */
-static JSON_Status skip_quotes(const char **string);
-static JSON_Status parse_utf16(const char **unprocessed, char **processed);
-static char * process_string(const char *input, size_t input_len, size_t *output_len);
-static char * get_quoted_string(const char **string, size_t *output_string_len);
-static JSON_Value * parse_object_value(const char **string, size_t nesting);
-static JSON_Value * parse_array_value(const char **string, size_t nesting);
-static JSON_Value * parse_string_value(const char **string);
-static JSON_Value * parse_boolean_value(const char **string);
-static JSON_Value * parse_number_value(const char **string);
-static JSON_Value * parse_null_value(const char **string);
-static JSON_Value * parse_value(const char **string, size_t nesting);
-
-/* Serialization */
-static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, parson_bool_t is_pretty, char *num_buf);
-static int json_serialize_string(const char *string, size_t len, char *buf);
-
-/* Various */
-static char * read_file(const char * filename) {
- FILE *fp = fopen(filename, "r");
- size_t size_to_read = 0;
- size_t size_read = 0;
- long pos;
- char *file_contents;
- if (!fp) {
- return NULL;
- }
- fseek(fp, 0L, SEEK_END);
- pos = ftell(fp);
- if (pos < 0) {
- fclose(fp);
- return NULL;
- }
- size_to_read = pos;
- rewind(fp);
- file_contents = (char*)parson_malloc(sizeof(char) * (size_to_read + 1));
- if (!file_contents) {
- fclose(fp);
- return NULL;
- }
- size_read = fread(file_contents, 1, size_to_read, fp);
- if (size_read == 0 || ferror(fp)) {
- fclose(fp);
- parson_free(file_contents);
- return NULL;
- }
- fclose(fp);
- file_contents[size_read] = '\0';
- return file_contents;
-}
-
-static void remove_comments(char *string, const char *start_token, const char *end_token) {
- parson_bool_t in_string = PARSON_FALSE, escaped = PARSON_FALSE;
- size_t i;
- char *ptr = NULL, current_char;
- size_t start_token_len = strlen(start_token);
- size_t end_token_len = strlen(end_token);
- if (start_token_len == 0 || end_token_len == 0) {
- return;
- }
- while ((current_char = *string) != '\0') {
- if (current_char == '\\' && !escaped) {
- escaped = PARSON_TRUE;
- string++;
- continue;
- } else if (current_char == '\"' && !escaped) {
- in_string = !in_string;
- } else if (!in_string && strncmp(string, start_token, start_token_len) == 0) {
- for(i = 0; i < start_token_len; i++) {
- string[i] = ' ';
- }
- string = string + start_token_len;
- ptr = strstr(string, end_token);
- if (!ptr) {
- return;
- }
- for (i = 0; i < (ptr - string) + end_token_len; i++) {
- string[i] = ' ';
- }
- string = ptr + end_token_len - 1;
- }
- escaped = PARSON_FALSE;
- string++;
- }
-}
-
-static char * parson_strndup(const char *string, size_t n) {
- /* We expect the caller has validated that 'n' fits within the input buffer. */
- char *output_string = (char*)parson_malloc(n + 1);
- if (!output_string) {
- return NULL;
- }
- output_string[n] = '\0';
- memcpy(output_string, string, n);
- return output_string;
-}
-
-static char * parson_strdup(const char *string) {
- return parson_strndup(string, strlen(string));
-}
-
-static int hex_char_to_int(char c) {
- if (c >= '0' && c <= '9') {
- return c - '0';
- } else if (c >= 'a' && c <= 'f') {
- return c - 'a' + 10;
- } else if (c >= 'A' && c <= 'F') {
- return c - 'A' + 10;
- }
- return -1;
-}
-
-static JSON_Status parse_utf16_hex(const char *s, unsigned int *result) {
- int x1, x2, x3, x4;
- if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0' || s[3] == '\0') {
- return JSONFailure;
- }
- x1 = hex_char_to_int(s[0]);
- x2 = hex_char_to_int(s[1]);
- x3 = hex_char_to_int(s[2]);
- x4 = hex_char_to_int(s[3]);
- if (x1 == -1 || x2 == -1 || x3 == -1 || x4 == -1) {
- return JSONFailure;
- }
- *result = (unsigned int)((x1 << 12) | (x2 << 8) | (x3 << 4) | x4);
- return JSONSuccess;
-}
-
-static int num_bytes_in_utf8_sequence(unsigned char c) {
- if (c == 0xC0 || c == 0xC1 || c > 0xF4 || IS_CONT(c)) {
- return 0;
- } else if ((c & 0x80) == 0) { /* 0xxxxxxx */
- return 1;
- } else if ((c & 0xE0) == 0xC0) { /* 110xxxxx */
- return 2;
- } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx */
- return 3;
- } else if ((c & 0xF8) == 0xF0) { /* 11110xxx */
- return 4;
- }
- return 0; /* won't happen */
-}
-
-static JSON_Status verify_utf8_sequence(const unsigned char *string, int *len) {
- unsigned int cp = 0;
- *len = num_bytes_in_utf8_sequence(string[0]);
-
- if (*len == 1) {
- cp = string[0];
- } else if (*len == 2 && IS_CONT(string[1])) {
- cp = string[0] & 0x1F;
- cp = (cp << 6) | (string[1] & 0x3F);
- } else if (*len == 3 && IS_CONT(string[1]) && IS_CONT(string[2])) {
- cp = ((unsigned char)string[0]) & 0xF;
- cp = (cp << 6) | (string[1] & 0x3F);
- cp = (cp << 6) | (string[2] & 0x3F);
- } else if (*len == 4 && IS_CONT(string[1]) && IS_CONT(string[2]) && IS_CONT(string[3])) {
- cp = string[0] & 0x7;
- cp = (cp << 6) | (string[1] & 0x3F);
- cp = (cp << 6) | (string[2] & 0x3F);
- cp = (cp << 6) | (string[3] & 0x3F);
- } else {
- return JSONFailure;
- }
-
- /* overlong encodings */
- if ((cp < 0x80 && *len > 1) ||
- (cp < 0x800 && *len > 2) ||
- (cp < 0x10000 && *len > 3)) {
- return JSONFailure;
- }
-
- /* invalid unicode */
- if (cp > 0x10FFFF) {
- return JSONFailure;
- }
-
- /* surrogate halves */
- if (cp >= 0xD800 && cp <= 0xDFFF) {
- return JSONFailure;
- }
-
- return JSONSuccess;
-}
-
-static int is_valid_utf8(const char *string, size_t string_len) {
- int len = 0;
- const char *string_end = string + string_len;
- while (string < string_end) {
- if (verify_utf8_sequence((const unsigned char*)string, &len) != JSONSuccess) {
- return PARSON_FALSE;
- }
- string += len;
- }
- return PARSON_TRUE;
-}
-
-static parson_bool_t is_decimal(const char *string, size_t length) {
- if (length > 1 && string[0] == '0' && string[1] != '.') {
- return PARSON_FALSE;
- }
- if (length > 2 && !strncmp(string, "-0", 2) && string[2] != '.') {
- return PARSON_FALSE;
- }
- while (length--) {
- if (strchr("xX", string[length])) {
- return PARSON_FALSE;
- }
- }
- return PARSON_TRUE;
-}
-
-static unsigned long hash_string(const char *string, size_t n) {
-#ifdef PARSON_FORCE_HASH_COLLISIONS
- (void)string;
- (void)n;
- return 0;
-#else
- unsigned long hash = 5381;
- unsigned char c;
- size_t i = 0;
- for (i = 0; i < n; i++) {
- c = string[i];
- if (c == '\0') {
- break;
- }
- hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
- }
- return hash;
-#endif
-}
-
-/* JSON Object */
-static JSON_Object * json_object_make(JSON_Value *wrapping_value) {
- JSON_Status res = JSONFailure;
- JSON_Object *new_obj = (JSON_Object*)parson_malloc(sizeof(JSON_Object));
- if (new_obj == NULL) {
- return NULL;
- }
- new_obj->wrapping_value = wrapping_value;
- res = json_object_init(new_obj, 0);
- if (res != JSONSuccess) {
- parson_free(new_obj);
- return NULL;
- }
- return new_obj;
-}
-
-static JSON_Status json_object_init(JSON_Object *object, size_t capacity) {
- unsigned int i = 0;
-
- object->cells = NULL;
- object->names = NULL;
- object->values = NULL;
- object->cell_ixs = NULL;
- object->hashes = NULL;
-
- object->count = 0;
- object->cell_capacity = capacity;
- object->item_capacity = (unsigned int)(capacity * 7/10);
-
- if (capacity == 0) {
- return JSONSuccess;
- }
-
- object->cells = (size_t*)parson_malloc(object->cell_capacity * sizeof(*object->cells));
- object->names = (char**)parson_malloc(object->item_capacity * sizeof(*object->names));
- object->values = (JSON_Value**)parson_malloc(object->item_capacity * sizeof(*object->values));
- object->cell_ixs = (size_t*)parson_malloc(object->item_capacity * sizeof(*object->cell_ixs));
- object->hashes = (unsigned long*)parson_malloc(object->item_capacity * sizeof(*object->hashes));
- if (object->cells == NULL
- || object->names == NULL
- || object->values == NULL
- || object->cell_ixs == NULL
- || object->hashes == NULL) {
- goto error;
- }
- for (i = 0; i < object->cell_capacity; i++) {
- object->cells[i] = OBJECT_INVALID_IX;
- }
- return JSONSuccess;
-error:
- parson_free(object->cells);
- parson_free(object->names);
- parson_free(object->values);
- parson_free(object->cell_ixs);
- parson_free(object->hashes);
- return JSONFailure;
-}
-
-static void json_object_deinit(JSON_Object *object, parson_bool_t free_keys, parson_bool_t free_values) {
- unsigned int i = 0;
- for (i = 0; i < object->count; i++) {
- if (free_keys) {
- parson_free(object->names[i]);
- }
- if (free_values) {
- json_value_free(object->values[i]);
- }
- }
-
- object->count = 0;
- object->item_capacity = 0;
- object->cell_capacity = 0;
-
- parson_free(object->cells);
- parson_free(object->names);
- parson_free(object->values);
- parson_free(object->cell_ixs);
- parson_free(object->hashes);
-
- object->cells = NULL;
- object->names = NULL;
- object->values = NULL;
- object->cell_ixs = NULL;
- object->hashes = NULL;
-}
-
-static JSON_Status json_object_grow_and_rehash(JSON_Object *object) {
- JSON_Value *wrapping_value = NULL;
- JSON_Object new_object;
- char *key = NULL;
- JSON_Value *value = NULL;
- unsigned int i = 0;
- size_t new_capacity = MAX(object->cell_capacity * 2, STARTING_CAPACITY);
- JSON_Status res = json_object_init(&new_object, new_capacity);
- if (res != JSONSuccess) {
- return JSONFailure;
- }
-
- wrapping_value = json_object_get_wrapping_value(object);
- new_object.wrapping_value = wrapping_value;
-
- for (i = 0; i < object->count; i++) {
- key = object->names[i];
- value = object->values[i];
- res = json_object_add(&new_object, key, value);
- if (res != JSONSuccess) {
- json_object_deinit(&new_object, PARSON_FALSE, PARSON_FALSE);
- return JSONFailure;
- }
- value->parent = wrapping_value;
- }
- json_object_deinit(object, PARSON_FALSE, PARSON_FALSE);
- *object = new_object;
- return JSONSuccess;
-}
-
-static size_t json_object_get_cell_ix(const JSON_Object *object, const char *key, size_t key_len, unsigned long hash, parson_bool_t *out_found) {
- size_t cell_ix = hash & (object->cell_capacity - 1);
- size_t cell = 0;
- size_t ix = 0;
- unsigned int i = 0;
- unsigned long hash_to_check = 0;
- const char *key_to_check = NULL;
- size_t key_to_check_len = 0;
-
- *out_found = PARSON_FALSE;
-
- for (i = 0; i < object->cell_capacity; i++) {
- ix = (cell_ix + i) & (object->cell_capacity - 1);
- cell = object->cells[ix];
- if (cell == OBJECT_INVALID_IX) {
- return ix;
- }
- hash_to_check = object->hashes[cell];
- if (hash != hash_to_check) {
- continue;
- }
- key_to_check = object->names[cell];
- key_to_check_len = strlen(key_to_check);
- if (key_to_check_len == key_len && strncmp(key, key_to_check, key_len) == 0) {
- *out_found = PARSON_TRUE;
- return ix;
- }
- }
- return OBJECT_INVALID_IX;
-}
-
-static JSON_Status json_object_add(JSON_Object *object, char *name, JSON_Value *value) {
- unsigned long hash = 0;
- parson_bool_t found = PARSON_FALSE;
- size_t cell_ix = 0;
- JSON_Status res = JSONFailure;
-
- if (!object || !name || !value) {
- return JSONFailure;
- }
-
- hash = hash_string(name, strlen(name));
- found = PARSON_FALSE;
- cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);
- if (found) {
- return JSONFailure;
- }
-
- if (object->count >= object->item_capacity) {
- res = json_object_grow_and_rehash(object);
- if (res != JSONSuccess) {
- return JSONFailure;
- }
- cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);
- }
-
- object->names[object->count] = name;
- object->cells[cell_ix] = object->count;
- object->values[object->count] = value;
- object->cell_ixs[object->count] = cell_ix;
- object->hashes[object->count] = hash;
- object->count++;
- value->parent = json_object_get_wrapping_value(object);
-
- return JSONSuccess;
-}
-
-static JSON_Value * json_object_getn_value(const JSON_Object *object, const char *name, size_t name_len) {
- unsigned long hash = 0;
- parson_bool_t found = PARSON_FALSE;
- size_t cell_ix = 0;
- size_t item_ix = 0;
- if (!object || !name) {
- return NULL;
- }
- hash = hash_string(name, name_len);
- found = PARSON_FALSE;
- cell_ix = json_object_get_cell_ix(object, name, name_len, hash, &found);
- if (!found) {
- return NULL;
- }
- item_ix = object->cells[cell_ix];
- return object->values[item_ix];
-}
-
-static JSON_Status json_object_remove_internal(JSON_Object *object, const char *name, parson_bool_t free_value) {
- unsigned long hash = 0;
- parson_bool_t found = PARSON_FALSE;
- size_t cell = 0;
- size_t item_ix = 0;
- size_t last_item_ix = 0;
- size_t i = 0;
- size_t j = 0;
- size_t x = 0;
- size_t k = 0;
- JSON_Value *val = NULL;
-
- if (object == NULL) {
- return JSONFailure;
- }
-
- hash = hash_string(name, strlen(name));
- found = PARSON_FALSE;
- cell = json_object_get_cell_ix(object, name, strlen(name), hash, &found);
- if (!found) {
- return JSONFailure;
- }
-
- item_ix = object->cells[cell];
- if (free_value) {
- val = object->values[item_ix];
- json_value_free(val);
- val = NULL;
- }
-
- parson_free(object->names[item_ix]);
- last_item_ix = object->count - 1;
- if (item_ix < last_item_ix) {
- object->names[item_ix] = object->names[last_item_ix];
- object->values[item_ix] = object->values[last_item_ix];
- object->cell_ixs[item_ix] = object->cell_ixs[last_item_ix];
- object->hashes[item_ix] = object->hashes[last_item_ix];
- object->cells[object->cell_ixs[item_ix]] = item_ix;
- }
- object->count--;
-
- i = cell;
- j = i;
- for (x = 0; x < (object->cell_capacity - 1); x++) {
- j = (j + 1) & (object->cell_capacity - 1);
- if (object->cells[j] == OBJECT_INVALID_IX) {
- break;
- }
- k = object->hashes[object->cells[j]] & (object->cell_capacity - 1);
- if ((j > i && (k <= i || k > j))
- || (j < i && (k <= i && k > j))) {
- object->cell_ixs[object->cells[j]] = i;
- object->cells[i] = object->cells[j];
- i = j;
- }
- }
- object->cells[i] = OBJECT_INVALID_IX;
- return JSONSuccess;
-}
-
-static JSON_Status json_object_dotremove_internal(JSON_Object *object, const char *name, parson_bool_t free_value) {
- JSON_Value *temp_value = NULL;
- JSON_Object *temp_object = NULL;
- const char *dot_pos = strchr(name, '.');
- if (!dot_pos) {
- return json_object_remove_internal(object, name, free_value);
- }
- temp_value = json_object_getn_value(object, name, dot_pos - name);
- if (json_value_get_type(temp_value) != JSONObject) {
- return JSONFailure;
- }
- temp_object = json_value_get_object(temp_value);
- return json_object_dotremove_internal(temp_object, dot_pos + 1, free_value);
-}
-
-static void json_object_free(JSON_Object *object) {
- json_object_deinit(object, PARSON_TRUE, PARSON_TRUE);
- parson_free(object);
-}
-
-/* JSON Array */
-static JSON_Array * json_array_make(JSON_Value *wrapping_value) {
- JSON_Array *new_array = (JSON_Array*)parson_malloc(sizeof(JSON_Array));
- if (new_array == NULL) {
- return NULL;
- }
- new_array->wrapping_value = wrapping_value;
- new_array->items = (JSON_Value**)NULL;
- new_array->capacity = 0;
- new_array->count = 0;
- return new_array;
-}
-
-static JSON_Status json_array_add(JSON_Array *array, JSON_Value *value) {
- if (array->count >= array->capacity) {
- size_t new_capacity = MAX(array->capacity * 2, STARTING_CAPACITY);
- if (json_array_resize(array, new_capacity) != JSONSuccess) {
- return JSONFailure;
- }
- }
- value->parent = json_array_get_wrapping_value(array);
- array->items[array->count] = value;
- array->count++;
- return JSONSuccess;
-}
-
-static JSON_Status json_array_resize(JSON_Array *array, size_t new_capacity) {
- JSON_Value **new_items = NULL;
- if (new_capacity == 0) {
- return JSONFailure;
- }
- new_items = (JSON_Value**)parson_malloc(new_capacity * sizeof(JSON_Value*));
- if (new_items == NULL) {
- return JSONFailure;
- }
- if (array->items != NULL && array->count > 0) {
- memcpy(new_items, array->items, array->count * sizeof(JSON_Value*));
- }
- parson_free(array->items);
- array->items = new_items;
- array->capacity = new_capacity;
- return JSONSuccess;
-}
-
-static void json_array_free(JSON_Array *array) {
- size_t i;
- for (i = 0; i < array->count; i++) {
- json_value_free(array->items[i]);
- }
- parson_free(array->items);
- parson_free(array);
-}
-
-/* JSON Value */
-static JSON_Value * json_value_init_string_no_copy(char *string, size_t length) {
- JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
- if (!new_value) {
- return NULL;
- }
- new_value->parent = NULL;
- new_value->type = JSONString;
- new_value->value.string.chars = string;
- new_value->value.string.length = length;
- return new_value;
-}
-
-/* Parser */
-static JSON_Status skip_quotes(const char **string) {
- if (**string != '\"') {
- return JSONFailure;
- }
- SKIP_CHAR(string);
- while (**string != '\"') {
- if (**string == '\0') {
- return JSONFailure;
- } else if (**string == '\\') {
- SKIP_CHAR(string);
- if (**string == '\0') {
- return JSONFailure;
- }
- }
- SKIP_CHAR(string);
- }
- SKIP_CHAR(string);
- return JSONSuccess;
-}
-
-static JSON_Status parse_utf16(const char **unprocessed, char **processed) {
- unsigned int cp, lead, trail;
- char *processed_ptr = *processed;
- const char *unprocessed_ptr = *unprocessed;
- JSON_Status status = JSONFailure;
- unprocessed_ptr++; /* skips u */
- status = parse_utf16_hex(unprocessed_ptr, &cp);
- if (status != JSONSuccess) {
- return JSONFailure;
- }
- if (cp < 0x80) {
- processed_ptr[0] = (char)cp; /* 0xxxxxxx */
- } else if (cp < 0x800) {
- processed_ptr[0] = ((cp >> 6) & 0x1F) | 0xC0; /* 110xxxxx */
- processed_ptr[1] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */
- processed_ptr += 1;
- } else if (cp < 0xD800 || cp > 0xDFFF) {
- processed_ptr[0] = ((cp >> 12) & 0x0F) | 0xE0; /* 1110xxxx */
- processed_ptr[1] = ((cp >> 6) & 0x3F) | 0x80; /* 10xxxxxx */
- processed_ptr[2] = ((cp) & 0x3F) | 0x80; /* 10xxxxxx */
- processed_ptr += 2;
- } else if (cp >= 0xD800 && cp <= 0xDBFF) { /* lead surrogate (0xD800..0xDBFF) */
- lead = cp;
- unprocessed_ptr += 4; /* should always be within the buffer, otherwise previous sscanf would fail */
- if (*unprocessed_ptr++ != '\\' || *unprocessed_ptr++ != 'u') {
- return JSONFailure;
- }
- status = parse_utf16_hex(unprocessed_ptr, &trail);
- if (status != JSONSuccess || trail < 0xDC00 || trail > 0xDFFF) { /* valid trail surrogate? (0xDC00..0xDFFF) */
- return JSONFailure;
- }
- cp = ((((lead - 0xD800) & 0x3FF) << 10) | ((trail - 0xDC00) & 0x3FF)) + 0x010000;
- processed_ptr[0] = (((cp >> 18) & 0x07) | 0xF0); /* 11110xxx */
- processed_ptr[1] = (((cp >> 12) & 0x3F) | 0x80); /* 10xxxxxx */
- processed_ptr[2] = (((cp >> 6) & 0x3F) | 0x80); /* 10xxxxxx */
- processed_ptr[3] = (((cp) & 0x3F) | 0x80); /* 10xxxxxx */
- processed_ptr += 3;
- } else { /* trail surrogate before lead surrogate */
- return JSONFailure;
- }
- unprocessed_ptr += 3;
- *processed = processed_ptr;
- *unprocessed = unprocessed_ptr;
- return JSONSuccess;
-}
-
-
-/* Copies and processes passed string up to supplied length.
-Example: "\u006Corem ipsum" -> lorem ipsum */
-static char* process_string(const char *input, size_t input_len, size_t *output_len) {
- const char *input_ptr = input;
- size_t initial_size = (input_len + 1) * sizeof(char);
- size_t final_size = 0;
- char *output = NULL, *output_ptr = NULL, *resized_output = NULL;
- output = (char*)parson_malloc(initial_size);
- if (output == NULL) {
- goto error;
- }
- output_ptr = output;
- while ((*input_ptr != '\0') && (size_t)(input_ptr - input) < input_len) {
- if (*input_ptr == '\\') {
- input_ptr++;
- switch (*input_ptr) {
- case '\"': *output_ptr = '\"'; break;
- case '\\': *output_ptr = '\\'; break;
- case '/': *output_ptr = '/'; break;
- case 'b': *output_ptr = '\b'; break;
- case 'f': *output_ptr = '\f'; break;
- case 'n': *output_ptr = '\n'; break;
- case 'r': *output_ptr = '\r'; break;
- case 't': *output_ptr = '\t'; break;
- case 'u':
- if (parse_utf16(&input_ptr, &output_ptr) != JSONSuccess) {
- goto error;
- }
- break;
- default:
- goto error;
- }
- } else if ((unsigned char)*input_ptr < 0x20) {
- goto error; /* 0x00-0x19 are invalid characters for json string (http://www.ietf.org/rfc/rfc4627.txt) */
- } else {
- *output_ptr = *input_ptr;
- }
- output_ptr++;
- input_ptr++;
- }
- *output_ptr = '\0';
- /* resize to new length */
- final_size = (size_t)(output_ptr-output) + 1;
- /* todo: don't resize if final_size == initial_size */
- resized_output = (char*)parson_malloc(final_size);
- if (resized_output == NULL) {
- goto error;
- }
- memcpy(resized_output, output, final_size);
- *output_len = final_size - 1;
- parson_free(output);
- return resized_output;
-error:
- parson_free(output);
- return NULL;
-}
-
-/* Return processed contents of a string between quotes and
- skips passed argument to a matching quote. */
-static char * get_quoted_string(const char **string, size_t *output_string_len) {
- const char *string_start = *string;
- size_t input_string_len = 0;
- JSON_Status status = skip_quotes(string);
- if (status != JSONSuccess) {
- return NULL;
- }
- input_string_len = *string - string_start - 2; /* length without quotes */
- return process_string(string_start + 1, input_string_len, output_string_len);
-}
-
-static JSON_Value * parse_value(const char **string, size_t nesting) {
- if (nesting > MAX_NESTING) {
- return NULL;
- }
- SKIP_WHITESPACES(string);
- switch (**string) {
- case '{':
- return parse_object_value(string, nesting + 1);
- case '[':
- return parse_array_value(string, nesting + 1);
- case '\"':
- return parse_string_value(string);
- case 'f': case 't':
- return parse_boolean_value(string);
- case '-':
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- return parse_number_value(string);
- case 'n':
- return parse_null_value(string);
- default:
- return NULL;
- }
-}
-
-static JSON_Value * parse_object_value(const char **string, size_t nesting) {
- JSON_Status status = JSONFailure;
- JSON_Value *output_value = NULL, *new_value = NULL;
- JSON_Object *output_object = NULL;
- char *new_key = NULL;
-
- output_value = json_value_init_object();
- if (output_value == NULL) {
- return NULL;
- }
- if (**string != '{') {
- json_value_free(output_value);
- return NULL;
- }
- output_object = json_value_get_object(output_value);
- SKIP_CHAR(string);
- SKIP_WHITESPACES(string);
- if (**string == '}') { /* empty object */
- SKIP_CHAR(string);
- return output_value;
- }
- while (**string != '\0') {
- size_t key_len = 0;
- new_key = get_quoted_string(string, &key_len);
- /* We do not support key names with embedded \0 chars */
- if (!new_key) {
- json_value_free(output_value);
- return NULL;
- }
- if (key_len != strlen(new_key)) {
- parson_free(new_key);
- json_value_free(output_value);
- return NULL;
- }
- SKIP_WHITESPACES(string);
- if (**string != ':') {
- parson_free(new_key);
- json_value_free(output_value);
- return NULL;
- }
- SKIP_CHAR(string);
- new_value = parse_value(string, nesting);
- if (new_value == NULL) {
- parson_free(new_key);
- json_value_free(output_value);
- return NULL;
- }
- status = json_object_add(output_object, new_key, new_value);
- if (status != JSONSuccess) {
- parson_free(new_key);
- json_value_free(new_value);
- json_value_free(output_value);
- return NULL;
- }
- SKIP_WHITESPACES(string);
- if (**string != ',') {
- break;
- }
- SKIP_CHAR(string);
- SKIP_WHITESPACES(string);
- if (**string == '}') {
- break;
- }
- }
- SKIP_WHITESPACES(string);
- if (**string != '}') {
- json_value_free(output_value);
- return NULL;
- }
- SKIP_CHAR(string);
- return output_value;
-}
-
-static JSON_Value * parse_array_value(const char **string, size_t nesting) {
- JSON_Value *output_value = NULL, *new_array_value = NULL;
- JSON_Array *output_array = NULL;
- output_value = json_value_init_array();
- if (output_value == NULL) {
- return NULL;
- }
- if (**string != '[') {
- json_value_free(output_value);
- return NULL;
- }
- output_array = json_value_get_array(output_value);
- SKIP_CHAR(string);
- SKIP_WHITESPACES(string);
- if (**string == ']') { /* empty array */
- SKIP_CHAR(string);
- return output_value;
- }
- while (**string != '\0') {
- new_array_value = parse_value(string, nesting);
- if (new_array_value == NULL) {
- json_value_free(output_value);
- return NULL;
- }
- if (json_array_add(output_array, new_array_value) != JSONSuccess) {
- json_value_free(new_array_value);
- json_value_free(output_value);
- return NULL;
- }
- SKIP_WHITESPACES(string);
- if (**string != ',') {
- break;
- }
- SKIP_CHAR(string);
- SKIP_WHITESPACES(string);
- if (**string == ']') {
- break;
- }
- }
- SKIP_WHITESPACES(string);
- if (**string != ']' || /* Trim array after parsing is over */
- json_array_resize(output_array, json_array_get_count(output_array)) != JSONSuccess) {
- json_value_free(output_value);
- return NULL;
- }
- SKIP_CHAR(string);
- return output_value;
-}
-
-static JSON_Value * parse_string_value(const char **string) {
- JSON_Value *value = NULL;
- size_t new_string_len = 0;
- char *new_string = get_quoted_string(string, &new_string_len);
- if (new_string == NULL) {
- return NULL;
- }
- value = json_value_init_string_no_copy(new_string, new_string_len);
- if (value == NULL) {
- parson_free(new_string);
- return NULL;
- }
- return value;
-}
-
-static JSON_Value * parse_boolean_value(const char **string) {
- size_t true_token_size = SIZEOF_TOKEN("true");
- size_t false_token_size = SIZEOF_TOKEN("false");
- if (strncmp("true", *string, true_token_size) == 0) {
- *string += true_token_size;
- return json_value_init_boolean(1);
- } else if (strncmp("false", *string, false_token_size) == 0) {
- *string += false_token_size;
- return json_value_init_boolean(0);
- }
- return NULL;
-}
-
-static JSON_Value * parse_number_value(const char **string) {
- char *end;
- double number = 0;
- errno = 0;
- number = strtod(*string, &end);
- if (errno == ERANGE && (number <= -HUGE_VAL || number >= HUGE_VAL)) {
- return NULL;
- }
- if ((errno && errno != ERANGE) || !is_decimal(*string, end - *string)) {
- return NULL;
- }
- *string = end;
- return json_value_init_number(number);
-}
-
-static JSON_Value * parse_null_value(const char **string) {
- size_t token_size = SIZEOF_TOKEN("null");
- if (strncmp("null", *string, token_size) == 0) {
- *string += token_size;
- return json_value_init_null();
- }
- return NULL;
-}
-
-/* Serialization */
-
-/* APPEND_STRING() is only called on string literals.
- It's a bit hacky because it makes plenty of assumptions about the external state
- and should eventually be tidied up into a function (same goes for APPEND_INDENT)
- */
-#define APPEND_STRING(str) do {\
- written = SIZEOF_TOKEN((str));\
- if (buf != NULL) {\
- memcpy(buf, (str), written);\
- buf[written] = '\0';\
- buf += written;\
- }\
- written_total += written;\
- } while (0)
-
-#define APPEND_INDENT(level) do {\
- int level_i = 0;\
- for (level_i = 0; level_i < (level); level_i++) {\
- APPEND_STRING(PARSON_INDENT_STR);\
- }\
- } while (0)
-
-static int json_serialize_to_buffer_r(const JSON_Value *value, char *buf, int level, parson_bool_t is_pretty, char *num_buf)
-{
- const char *key = NULL, *string = NULL;
- JSON_Value *temp_value = NULL;
- JSON_Array *array = NULL;
- JSON_Object *object = NULL;
- size_t i = 0, count = 0;
- double num = 0.0;
- int written = -1, written_total = 0;
- size_t len = 0;
-
- switch (json_value_get_type(value)) {
- case JSONArray:
- array = json_value_get_array(value);
- count = json_array_get_count(array);
- APPEND_STRING("[");
- if (count > 0 && is_pretty) {
- APPEND_STRING("\n");
- }
- for (i = 0; i < count; i++) {
- if (is_pretty) {
- APPEND_INDENT(level+1);
- }
- temp_value = json_array_get_value(array, i);
- written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf);
- if (written < 0) {
- return -1;
- }
- if (buf != NULL) {
- buf += written;
- }
- written_total += written;
- if (i < (count - 1)) {
- APPEND_STRING(",");
- }
- if (is_pretty) {
- APPEND_STRING("\n");
- }
- }
- if (count > 0 && is_pretty) {
- APPEND_INDENT(level);
- }
- APPEND_STRING("]");
- return written_total;
- case JSONObject:
- object = json_value_get_object(value);
- count = json_object_get_count(object);
- APPEND_STRING("{");
- if (count > 0 && is_pretty) {
- APPEND_STRING("\n");
- }
- for (i = 0; i < count; i++) {
- key = json_object_get_name(object, i);
- if (key == NULL) {
- return -1;
- }
- if (is_pretty) {
- APPEND_INDENT(level+1);
- }
- /* We do not support key names with embedded \0 chars */
- written = json_serialize_string(key, strlen(key), buf);
- if (written < 0) {
- return -1;
- }
- if (buf != NULL) {
- buf += written;
- }
- written_total += written;
- APPEND_STRING(":");
- if (is_pretty) {
- APPEND_STRING(" ");
- }
- temp_value = json_object_get_value_at(object, i);
- written = json_serialize_to_buffer_r(temp_value, buf, level+1, is_pretty, num_buf);
- if (written < 0) {
- return -1;
- }
- if (buf != NULL) {
- buf += written;
- }
- written_total += written;
- if (i < (count - 1)) {
- APPEND_STRING(",");
- }
- if (is_pretty) {
- APPEND_STRING("\n");
- }
- }
- if (count > 0 && is_pretty) {
- APPEND_INDENT(level);
- }
- APPEND_STRING("}");
- return written_total;
- case JSONString:
- string = json_value_get_string(value);
- if (string == NULL) {
- return -1;
- }
- len = json_value_get_string_len(value);
- written = json_serialize_string(string, len, buf);
- if (written < 0) {
- return -1;
- }
- if (buf != NULL) {
- buf += written;
- }
- written_total += written;
- return written_total;
- case JSONBoolean:
- if (json_value_get_boolean(value)) {
- APPEND_STRING("true");
- } else {
- APPEND_STRING("false");
- }
- return written_total;
- case JSONNumber:
- num = json_value_get_number(value);
- if (buf != NULL) {
- num_buf = buf;
- }
- if (parson_number_serialization_function) {
- written = parson_number_serialization_function(num, num_buf);
- } else if (parson_float_format) {
- written = sprintf(num_buf, parson_float_format, num);
- } else {
- written = sprintf(num_buf, PARSON_DEFAULT_FLOAT_FORMAT, num);
- }
- if (written < 0) {
- return -1;
- }
- if (buf != NULL) {
- buf += written;
- }
- written_total += written;
- return written_total;
- case JSONNull:
- APPEND_STRING("null");
- return written_total;
- case JSONError:
- return -1;
- default:
- return -1;
- }
-}
-
-static int json_serialize_string(const char *string, size_t len, char *buf) {
- size_t i = 0;
- char c = '\0';
- int written = -1, written_total = 0;
- APPEND_STRING("\"");
- for (i = 0; i < len; i++) {
- c = string[i];
- switch (c) {
- case '\"': APPEND_STRING("\\\""); break;
- case '\\': APPEND_STRING("\\\\"); break;
- case '\b': APPEND_STRING("\\b"); break;
- case '\f': APPEND_STRING("\\f"); break;
- case '\n': APPEND_STRING("\\n"); break;
- case '\r': APPEND_STRING("\\r"); break;
- case '\t': APPEND_STRING("\\t"); break;
- case '\x00': APPEND_STRING("\\u0000"); break;
- case '\x01': APPEND_STRING("\\u0001"); break;
- case '\x02': APPEND_STRING("\\u0002"); break;
- case '\x03': APPEND_STRING("\\u0003"); break;
- case '\x04': APPEND_STRING("\\u0004"); break;
- case '\x05': APPEND_STRING("\\u0005"); break;
- case '\x06': APPEND_STRING("\\u0006"); break;
- case '\x07': APPEND_STRING("\\u0007"); break;
- /* '\x08' duplicate: '\b' */
- /* '\x09' duplicate: '\t' */
- /* '\x0a' duplicate: '\n' */
- case '\x0b': APPEND_STRING("\\u000b"); break;
- /* '\x0c' duplicate: '\f' */
- /* '\x0d' duplicate: '\r' */
- case '\x0e': APPEND_STRING("\\u000e"); break;
- case '\x0f': APPEND_STRING("\\u000f"); break;
- case '\x10': APPEND_STRING("\\u0010"); break;
- case '\x11': APPEND_STRING("\\u0011"); break;
- case '\x12': APPEND_STRING("\\u0012"); break;
- case '\x13': APPEND_STRING("\\u0013"); break;
- case '\x14': APPEND_STRING("\\u0014"); break;
- case '\x15': APPEND_STRING("\\u0015"); break;
- case '\x16': APPEND_STRING("\\u0016"); break;
- case '\x17': APPEND_STRING("\\u0017"); break;
- case '\x18': APPEND_STRING("\\u0018"); break;
- case '\x19': APPEND_STRING("\\u0019"); break;
- case '\x1a': APPEND_STRING("\\u001a"); break;
- case '\x1b': APPEND_STRING("\\u001b"); break;
- case '\x1c': APPEND_STRING("\\u001c"); break;
- case '\x1d': APPEND_STRING("\\u001d"); break;
- case '\x1e': APPEND_STRING("\\u001e"); break;
- case '\x1f': APPEND_STRING("\\u001f"); break;
- case '/':
- if (parson_escape_slashes) {
- APPEND_STRING("\\/"); /* to make json embeddable in xml\/html */
- } else {
- APPEND_STRING("/");
- }
- break;
- default:
- if (buf != NULL) {
- buf[0] = c;
- buf += 1;
- }
- written_total += 1;
- break;
- }
- }
- APPEND_STRING("\"");
- return written_total;
-}
-
-#undef APPEND_STRING
-#undef APPEND_INDENT
-
-/* Parser API */
-JSON_Value * json_parse_file(const char *filename) {
- char *file_contents = read_file(filename);
- JSON_Value *output_value = NULL;
- if (file_contents == NULL) {
- return NULL;
- }
- output_value = json_parse_string(file_contents);
- parson_free(file_contents);
- return output_value;
-}
-
-JSON_Value * json_parse_file_with_comments(const char *filename) {
- char *file_contents = read_file(filename);
- JSON_Value *output_value = NULL;
- if (file_contents == NULL) {
- return NULL;
- }
- output_value = json_parse_string_with_comments(file_contents);
- parson_free(file_contents);
- return output_value;
-}
-
-JSON_Value * json_parse_string(const char *string) {
- if (string == NULL) {
- return NULL;
- }
- if (string[0] == '\xEF' && string[1] == '\xBB' && string[2] == '\xBF') {
- string = string + 3; /* Support for UTF-8 BOM */
- }
- return parse_value((const char**)&string, 0);
-}
-
-JSON_Value * json_parse_string_with_comments(const char *string) {
- JSON_Value *result = NULL;
- char *string_mutable_copy = NULL, *string_mutable_copy_ptr = NULL;
- string_mutable_copy = parson_strdup(string);
- if (string_mutable_copy == NULL) {
- return NULL;
- }
- remove_comments(string_mutable_copy, "/*", "*/");
- remove_comments(string_mutable_copy, "//", "\n");
- string_mutable_copy_ptr = string_mutable_copy;
- result = parse_value((const char**)&string_mutable_copy_ptr, 0);
- parson_free(string_mutable_copy);
- return result;
-}
-
-/* JSON Object API */
-
-JSON_Value * json_object_get_value(const JSON_Object *object, const char *name) {
- if (object == NULL || name == NULL) {
- return NULL;
- }
- return json_object_getn_value(object, name, strlen(name));
-}
-
-const char * json_object_get_string(const JSON_Object *object, const char *name) {
- return json_value_get_string(json_object_get_value(object, name));
-}
-
-size_t json_object_get_string_len(const JSON_Object *object, const char *name) {
- return json_value_get_string_len(json_object_get_value(object, name));
-}
-
-double json_object_get_number(const JSON_Object *object, const char *name) {
- return json_value_get_number(json_object_get_value(object, name));
-}
-
-JSON_Object * json_object_get_object(const JSON_Object *object, const char *name) {
- return json_value_get_object(json_object_get_value(object, name));
-}
-
-JSON_Array * json_object_get_array(const JSON_Object *object, const char *name) {
- return json_value_get_array(json_object_get_value(object, name));
-}
-
-int json_object_get_boolean(const JSON_Object *object, const char *name) {
- return json_value_get_boolean(json_object_get_value(object, name));
-}
-
-JSON_Value * json_object_dotget_value(const JSON_Object *object, const char *name) {
- const char *dot_position = strchr(name, '.');
- if (!dot_position) {
- return json_object_get_value(object, name);
- }
- object = json_value_get_object(json_object_getn_value(object, name, dot_position - name));
- return json_object_dotget_value(object, dot_position + 1);
-}
-
-const char * json_object_dotget_string(const JSON_Object *object, const char *name) {
- return json_value_get_string(json_object_dotget_value(object, name));
-}
-
-size_t json_object_dotget_string_len(const JSON_Object *object, const char *name) {
- return json_value_get_string_len(json_object_dotget_value(object, name));
-}
-
-double json_object_dotget_number(const JSON_Object *object, const char *name) {
- return json_value_get_number(json_object_dotget_value(object, name));
-}
-
-JSON_Object * json_object_dotget_object(const JSON_Object *object, const char *name) {
- return json_value_get_object(json_object_dotget_value(object, name));
-}
-
-JSON_Array * json_object_dotget_array(const JSON_Object *object, const char *name) {
- return json_value_get_array(json_object_dotget_value(object, name));
-}
-
-int json_object_dotget_boolean(const JSON_Object *object, const char *name) {
- return json_value_get_boolean(json_object_dotget_value(object, name));
-}
-
-size_t json_object_get_count(const JSON_Object *object) {
- return object ? object->count : 0;
-}
-
-const char * json_object_get_name(const JSON_Object *object, size_t index) {
- if (object == NULL || index >= json_object_get_count(object)) {
- return NULL;
- }
- return object->names[index];
-}
-
-JSON_Value * json_object_get_value_at(const JSON_Object *object, size_t index) {
- if (object == NULL || index >= json_object_get_count(object)) {
- return NULL;
- }
- return object->values[index];
-}
-
-JSON_Value *json_object_get_wrapping_value(const JSON_Object *object) {
- if (!object) {
- return NULL;
- }
- return object->wrapping_value;
-}
-
-int json_object_has_value (const JSON_Object *object, const char *name) {
- return json_object_get_value(object, name) != NULL;
-}
-
-int json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type) {
- JSON_Value *val = json_object_get_value(object, name);
- return val != NULL && json_value_get_type(val) == type;
-}
-
-int json_object_dothas_value (const JSON_Object *object, const char *name) {
- return json_object_dotget_value(object, name) != NULL;
-}
-
-int json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type) {
- JSON_Value *val = json_object_dotget_value(object, name);
- return val != NULL && json_value_get_type(val) == type;
-}
-
-/* JSON Array API */
-JSON_Value * json_array_get_value(const JSON_Array *array, size_t index) {
- if (array == NULL || index >= json_array_get_count(array)) {
- return NULL;
- }
- return array->items[index];
-}
-
-const char * json_array_get_string(const JSON_Array *array, size_t index) {
- return json_value_get_string(json_array_get_value(array, index));
-}
-
-size_t json_array_get_string_len(const JSON_Array *array, size_t index) {
- return json_value_get_string_len(json_array_get_value(array, index));
-}
-
-double json_array_get_number(const JSON_Array *array, size_t index) {
- return json_value_get_number(json_array_get_value(array, index));
-}
-
-JSON_Object * json_array_get_object(const JSON_Array *array, size_t index) {
- return json_value_get_object(json_array_get_value(array, index));
-}
-
-JSON_Array * json_array_get_array(const JSON_Array *array, size_t index) {
- return json_value_get_array(json_array_get_value(array, index));
-}
-
-int json_array_get_boolean(const JSON_Array *array, size_t index) {
- return json_value_get_boolean(json_array_get_value(array, index));
-}
-
-size_t json_array_get_count(const JSON_Array *array) {
- return array ? array->count : 0;
-}
-
-JSON_Value * json_array_get_wrapping_value(const JSON_Array *array) {
- if (!array) {
- return NULL;
- }
- return array->wrapping_value;
-}
-
-/* JSON Value API */
-JSON_Value_Type json_value_get_type(const JSON_Value *value) {
- return value ? value->type : JSONError;
-}
-
-JSON_Object * json_value_get_object(const JSON_Value *value) {
- return json_value_get_type(value) == JSONObject ? value->value.object : NULL;
-}
-
-JSON_Array * json_value_get_array(const JSON_Value *value) {
- return json_value_get_type(value) == JSONArray ? value->value.array : NULL;
-}
-
-static const JSON_String * json_value_get_string_desc(const JSON_Value *value) {
- return json_value_get_type(value) == JSONString ? &value->value.string : NULL;
-}
-
-const char * json_value_get_string(const JSON_Value *value) {
- const JSON_String *str = json_value_get_string_desc(value);
- return str ? str->chars : NULL;
-}
-
-size_t json_value_get_string_len(const JSON_Value *value) {
- const JSON_String *str = json_value_get_string_desc(value);
- return str ? str->length : 0;
-}
-
-double json_value_get_number(const JSON_Value *value) {
- return json_value_get_type(value) == JSONNumber ? value->value.number : 0;
-}
-
-int json_value_get_boolean(const JSON_Value *value) {
- return json_value_get_type(value) == JSONBoolean ? value->value.boolean : -1;
-}
-
-JSON_Value * json_value_get_parent (const JSON_Value *value) {
- return value ? value->parent : NULL;
-}
-
-void json_value_free(JSON_Value *value) {
- switch (json_value_get_type(value)) {
- case JSONObject:
- json_object_free(value->value.object);
- break;
- case JSONString:
- parson_free(value->value.string.chars);
- break;
- case JSONArray:
- json_array_free(value->value.array);
- break;
- default:
- break;
- }
- parson_free(value);
-}
-
-JSON_Value * json_value_init_object(void) {
- JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
- if (!new_value) {
- return NULL;
- }
- new_value->parent = NULL;
- new_value->type = JSONObject;
- new_value->value.object = json_object_make(new_value);
- if (!new_value->value.object) {
- parson_free(new_value);
- return NULL;
- }
- return new_value;
-}
-
-JSON_Value * json_value_init_array(void) {
- JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
- if (!new_value) {
- return NULL;
- }
- new_value->parent = NULL;
- new_value->type = JSONArray;
- new_value->value.array = json_array_make(new_value);
- if (!new_value->value.array) {
- parson_free(new_value);
- return NULL;
- }
- return new_value;
-}
-
-JSON_Value * json_value_init_string(const char *string) {
- if (string == NULL) {
- return NULL;
- }
- return json_value_init_string_with_len(string, strlen(string));
-}
-
-JSON_Value * json_value_init_string_with_len(const char *string, size_t length) {
- char *copy = NULL;
- JSON_Value *value;
- if (string == NULL) {
- return NULL;
- }
- if (!is_valid_utf8(string, length)) {
- return NULL;
- }
- copy = parson_strndup(string, length);
- if (copy == NULL) {
- return NULL;
- }
- value = json_value_init_string_no_copy(copy, length);
- if (value == NULL) {
- parson_free(copy);
- }
- return value;
-}
-
-JSON_Value * json_value_init_number(double number) {
- JSON_Value *new_value = NULL;
- if (IS_NUMBER_INVALID(number)) {
- return NULL;
- }
- new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
- if (new_value == NULL) {
- return NULL;
- }
- new_value->parent = NULL;
- new_value->type = JSONNumber;
- new_value->value.number = number;
- return new_value;
-}
-
-JSON_Value * json_value_init_boolean(int boolean) {
- JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
- if (!new_value) {
- return NULL;
- }
- new_value->parent = NULL;
- new_value->type = JSONBoolean;
- new_value->value.boolean = boolean ? 1 : 0;
- return new_value;
-}
-
-JSON_Value * json_value_init_null(void) {
- JSON_Value *new_value = (JSON_Value*)parson_malloc(sizeof(JSON_Value));
- if (!new_value) {
- return NULL;
- }
- new_value->parent = NULL;
- new_value->type = JSONNull;
- return new_value;
-}
-
-JSON_Value * json_value_deep_copy(const JSON_Value *value) {
- size_t i = 0;
- JSON_Value *return_value = NULL, *temp_value_copy = NULL, *temp_value = NULL;
- const JSON_String *temp_string = NULL;
- const char *temp_key = NULL;
- char *temp_string_copy = NULL;
- JSON_Array *temp_array = NULL, *temp_array_copy = NULL;
- JSON_Object *temp_object = NULL, *temp_object_copy = NULL;
- JSON_Status res = JSONFailure;
- char *key_copy = NULL;
-
- switch (json_value_get_type(value)) {
- case JSONArray:
- temp_array = json_value_get_array(value);
- return_value = json_value_init_array();
- if (return_value == NULL) {
- return NULL;
- }
- temp_array_copy = json_value_get_array(return_value);
- for (i = 0; i < json_array_get_count(temp_array); i++) {
- temp_value = json_array_get_value(temp_array, i);
- temp_value_copy = json_value_deep_copy(temp_value);
- if (temp_value_copy == NULL) {
- json_value_free(return_value);
- return NULL;
- }
- if (json_array_add(temp_array_copy, temp_value_copy) != JSONSuccess) {
- json_value_free(return_value);
- json_value_free(temp_value_copy);
- return NULL;
- }
- }
- return return_value;
- case JSONObject:
- temp_object = json_value_get_object(value);
- return_value = json_value_init_object();
- if (!return_value) {
- return NULL;
- }
- temp_object_copy = json_value_get_object(return_value);
- for (i = 0; i < json_object_get_count(temp_object); i++) {
- temp_key = json_object_get_name(temp_object, i);
- temp_value = json_object_get_value(temp_object, temp_key);
- temp_value_copy = json_value_deep_copy(temp_value);
- if (!temp_value_copy) {
- json_value_free(return_value);
- return NULL;
- }
- key_copy = parson_strdup(temp_key);
- if (!key_copy) {
- json_value_free(temp_value_copy);
- json_value_free(return_value);
- return NULL;
- }
- res = json_object_add(temp_object_copy, key_copy, temp_value_copy);
- if (res != JSONSuccess) {
- parson_free(key_copy);
- json_value_free(temp_value_copy);
- json_value_free(return_value);
- return NULL;
- }
- }
- return return_value;
- case JSONBoolean:
- return json_value_init_boolean(json_value_get_boolean(value));
- case JSONNumber:
- return json_value_init_number(json_value_get_number(value));
- case JSONString:
- temp_string = json_value_get_string_desc(value);
- if (temp_string == NULL) {
- return NULL;
- }
- temp_string_copy = parson_strndup(temp_string->chars, temp_string->length);
- if (temp_string_copy == NULL) {
- return NULL;
- }
- return_value = json_value_init_string_no_copy(temp_string_copy, temp_string->length);
- if (return_value == NULL) {
- parson_free(temp_string_copy);
- }
- return return_value;
- case JSONNull:
- return json_value_init_null();
- case JSONError:
- return NULL;
- default:
- return NULL;
- }
-}
-
-size_t json_serialization_size(const JSON_Value *value) {
- char num_buf[PARSON_NUM_BUF_SIZE]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */
- int res = json_serialize_to_buffer_r(value, NULL, 0, PARSON_FALSE, num_buf);
- return res < 0 ? 0 : (size_t)(res) + 1;
-}
-
-JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) {
- int written = -1;
- size_t needed_size_in_bytes = json_serialization_size(value);
- if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) {
- return JSONFailure;
- }
- written = json_serialize_to_buffer_r(value, buf, 0, PARSON_FALSE, NULL);
- if (written < 0) {
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename) {
- JSON_Status return_code = JSONSuccess;
- FILE *fp = NULL;
- char *serialized_string = json_serialize_to_string(value);
- if (serialized_string == NULL) {
- return JSONFailure;
- }
- fp = fopen(filename, "w");
- if (fp == NULL) {
- json_free_serialized_string(serialized_string);
- return JSONFailure;
- }
- if (fputs(serialized_string, fp) == EOF) {
- return_code = JSONFailure;
- }
- if (fclose(fp) == EOF) {
- return_code = JSONFailure;
- }
- json_free_serialized_string(serialized_string);
- return return_code;
-}
-
-char * json_serialize_to_string(const JSON_Value *value) {
- JSON_Status serialization_result = JSONFailure;
- size_t buf_size_bytes = json_serialization_size(value);
- char *buf = NULL;
- if (buf_size_bytes == 0) {
- return NULL;
- }
- buf = (char*)parson_malloc(buf_size_bytes);
- if (buf == NULL) {
- return NULL;
- }
- serialization_result = json_serialize_to_buffer(value, buf, buf_size_bytes);
- if (serialization_result != JSONSuccess) {
- json_free_serialized_string(buf);
- return NULL;
- }
- return buf;
-}
-
-size_t json_serialization_size_pretty(const JSON_Value *value) {
- char num_buf[PARSON_NUM_BUF_SIZE]; /* recursively allocating buffer on stack is a bad idea, so let's do it only once */
- int res = json_serialize_to_buffer_r(value, NULL, 0, PARSON_TRUE, num_buf);
- return res < 0 ? 0 : (size_t)(res) + 1;
-}
-
-JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes) {
- int written = -1;
- size_t needed_size_in_bytes = json_serialization_size_pretty(value);
- if (needed_size_in_bytes == 0 || buf_size_in_bytes < needed_size_in_bytes) {
- return JSONFailure;
- }
- written = json_serialize_to_buffer_r(value, buf, 0, PARSON_TRUE, NULL);
- if (written < 0) {
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename) {
- JSON_Status return_code = JSONSuccess;
- FILE *fp = NULL;
- char *serialized_string = json_serialize_to_string_pretty(value);
- if (serialized_string == NULL) {
- return JSONFailure;
- }
- fp = fopen(filename, "w");
- if (fp == NULL) {
- json_free_serialized_string(serialized_string);
- return JSONFailure;
- }
- if (fputs(serialized_string, fp) == EOF) {
- return_code = JSONFailure;
- }
- if (fclose(fp) == EOF) {
- return_code = JSONFailure;
- }
- json_free_serialized_string(serialized_string);
- return return_code;
-}
-
-char * json_serialize_to_string_pretty(const JSON_Value *value) {
- JSON_Status serialization_result = JSONFailure;
- size_t buf_size_bytes = json_serialization_size_pretty(value);
- char *buf = NULL;
- if (buf_size_bytes == 0) {
- return NULL;
- }
- buf = (char*)parson_malloc(buf_size_bytes);
- if (buf == NULL) {
- return NULL;
- }
- serialization_result = json_serialize_to_buffer_pretty(value, buf, buf_size_bytes);
- if (serialization_result != JSONSuccess) {
- json_free_serialized_string(buf);
- return NULL;
- }
- return buf;
-}
-
-void json_free_serialized_string(char *string) {
- parson_free(string);
-}
-
-JSON_Status json_array_remove(JSON_Array *array, size_t ix) {
- size_t to_move_bytes = 0;
- if (array == NULL || ix >= json_array_get_count(array)) {
- return JSONFailure;
- }
- json_value_free(json_array_get_value(array, ix));
- to_move_bytes = (json_array_get_count(array) - 1 - ix) * sizeof(JSON_Value*);
- memmove(array->items + ix, array->items + ix + 1, to_move_bytes);
- array->count -= 1;
- return JSONSuccess;
-}
-
-JSON_Status json_array_replace_value(JSON_Array *array, size_t ix, JSON_Value *value) {
- if (array == NULL || value == NULL || value->parent != NULL || ix >= json_array_get_count(array)) {
- return JSONFailure;
- }
- json_value_free(json_array_get_value(array, ix));
- value->parent = json_array_get_wrapping_value(array);
- array->items[ix] = value;
- return JSONSuccess;
-}
-
-JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string) {
- JSON_Value *value = json_value_init_string(string);
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_array_replace_value(array, i, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_array_replace_string_with_len(JSON_Array *array, size_t i, const char *string, size_t len) {
- JSON_Value *value = json_value_init_string_with_len(string, len);
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_array_replace_value(array, i, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number) {
- JSON_Value *value = json_value_init_number(number);
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_array_replace_value(array, i, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean) {
- JSON_Value *value = json_value_init_boolean(boolean);
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_array_replace_value(array, i, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_array_replace_null(JSON_Array *array, size_t i) {
- JSON_Value *value = json_value_init_null();
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_array_replace_value(array, i, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_array_clear(JSON_Array *array) {
- size_t i = 0;
- if (array == NULL) {
- return JSONFailure;
- }
- for (i = 0; i < json_array_get_count(array); i++) {
- json_value_free(json_array_get_value(array, i));
- }
- array->count = 0;
- return JSONSuccess;
-}
-
-JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value) {
- if (array == NULL || value == NULL || value->parent != NULL) {
- return JSONFailure;
- }
- return json_array_add(array, value);
-}
-
-JSON_Status json_array_append_string(JSON_Array *array, const char *string) {
- JSON_Value *value = json_value_init_string(string);
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_array_append_value(array, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_array_append_string_with_len(JSON_Array *array, const char *string, size_t len) {
- JSON_Value *value = json_value_init_string_with_len(string, len);
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_array_append_value(array, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_array_append_number(JSON_Array *array, double number) {
- JSON_Value *value = json_value_init_number(number);
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_array_append_value(array, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_array_append_boolean(JSON_Array *array, int boolean) {
- JSON_Value *value = json_value_init_boolean(boolean);
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_array_append_value(array, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_array_append_null(JSON_Array *array) {
- JSON_Value *value = json_value_init_null();
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_array_append_value(array, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value) {
- unsigned long hash = 0;
- parson_bool_t found = PARSON_FALSE;
- size_t cell_ix = 0;
- size_t item_ix = 0;
- JSON_Value *old_value = NULL;
- char *key_copy = NULL;
-
- if (!object || !name || !value || value->parent) {
- return JSONFailure;
- }
- hash = hash_string(name, strlen(name));
- found = PARSON_FALSE;
- cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);
- if (found) {
- item_ix = object->cells[cell_ix];
- old_value = object->values[item_ix];
- json_value_free(old_value);
- object->values[item_ix] = value;
- value->parent = json_object_get_wrapping_value(object);
- return JSONSuccess;
- }
- if (object->count >= object->item_capacity) {
- JSON_Status res = json_object_grow_and_rehash(object);
- if (res != JSONSuccess) {
- return JSONFailure;
- }
- cell_ix = json_object_get_cell_ix(object, name, strlen(name), hash, &found);
- }
- key_copy = parson_strdup(name);
- if (!key_copy) {
- return JSONFailure;
- }
- object->names[object->count] = key_copy;
- object->cells[cell_ix] = object->count;
- object->values[object->count] = value;
- object->cell_ixs[object->count] = cell_ix;
- object->hashes[object->count] = hash;
- object->count++;
- value->parent = json_object_get_wrapping_value(object);
- return JSONSuccess;
-}
-
-JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string) {
- JSON_Value *value = json_value_init_string(string);
- JSON_Status status = json_object_set_value(object, name, value);
- if (status != JSONSuccess) {
- json_value_free(value);
- }
- return status;
-}
-
-JSON_Status json_object_set_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len) {
- JSON_Value *value = json_value_init_string_with_len(string, len);
- JSON_Status status = json_object_set_value(object, name, value);
- if (status != JSONSuccess) {
- json_value_free(value);
- }
- return status;
-}
-
-JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number) {
- JSON_Value *value = json_value_init_number(number);
- JSON_Status status = json_object_set_value(object, name, value);
- if (status != JSONSuccess) {
- json_value_free(value);
- }
- return status;
-}
-
-JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean) {
- JSON_Value *value = json_value_init_boolean(boolean);
- JSON_Status status = json_object_set_value(object, name, value);
- if (status != JSONSuccess) {
- json_value_free(value);
- }
- return status;
-}
-
-JSON_Status json_object_set_null(JSON_Object *object, const char *name) {
- JSON_Value *value = json_value_init_null();
- JSON_Status status = json_object_set_value(object, name, value);
- if (status != JSONSuccess) {
- json_value_free(value);
- }
- return status;
-}
-
-JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value) {
- const char *dot_pos = NULL;
- JSON_Value *temp_value = NULL, *new_value = NULL;
- JSON_Object *temp_object = NULL, *new_object = NULL;
- JSON_Status status = JSONFailure;
- size_t name_len = 0;
- char *name_copy = NULL;
-
- if (object == NULL || name == NULL || value == NULL) {
- return JSONFailure;
- }
- dot_pos = strchr(name, '.');
- if (dot_pos == NULL) {
- return json_object_set_value(object, name, value);
- }
- name_len = dot_pos - name;
- temp_value = json_object_getn_value(object, name, name_len);
- if (temp_value) {
- /* Don't overwrite existing non-object (unlike json_object_set_value, but it shouldn't be changed at this point) */
- if (json_value_get_type(temp_value) != JSONObject) {
- return JSONFailure;
- }
- temp_object = json_value_get_object(temp_value);
- return json_object_dotset_value(temp_object, dot_pos + 1, value);
- }
- new_value = json_value_init_object();
- if (new_value == NULL) {
- return JSONFailure;
- }
- new_object = json_value_get_object(new_value);
- status = json_object_dotset_value(new_object, dot_pos + 1, value);
- if (status != JSONSuccess) {
- json_value_free(new_value);
- return JSONFailure;
- }
- name_copy = parson_strndup(name, name_len);
- if (!name_copy) {
- json_object_dotremove_internal(new_object, dot_pos + 1, 0);
- json_value_free(new_value);
- return JSONFailure;
- }
- status = json_object_add(object, name_copy, new_value);
- if (status != JSONSuccess) {
- parson_free(name_copy);
- json_object_dotremove_internal(new_object, dot_pos + 1, 0);
- json_value_free(new_value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string) {
- JSON_Value *value = json_value_init_string(string);
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_object_dotset_value(object, name, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_object_dotset_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len) {
- JSON_Value *value = json_value_init_string_with_len(string, len);
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_object_dotset_value(object, name, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number) {
- JSON_Value *value = json_value_init_number(number);
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_object_dotset_value(object, name, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean) {
- JSON_Value *value = json_value_init_boolean(boolean);
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_object_dotset_value(object, name, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_object_dotset_null(JSON_Object *object, const char *name) {
- JSON_Value *value = json_value_init_null();
- if (value == NULL) {
- return JSONFailure;
- }
- if (json_object_dotset_value(object, name, value) != JSONSuccess) {
- json_value_free(value);
- return JSONFailure;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_object_remove(JSON_Object *object, const char *name) {
- return json_object_remove_internal(object, name, PARSON_TRUE);
-}
-
-JSON_Status json_object_dotremove(JSON_Object *object, const char *name) {
- return json_object_dotremove_internal(object, name, PARSON_TRUE);
-}
-
-JSON_Status json_object_clear(JSON_Object *object) {
- size_t i = 0;
- if (object == NULL) {
- return JSONFailure;
- }
- for (i = 0; i < json_object_get_count(object); i++) {
- parson_free(object->names[i]);
- object->names[i] = NULL;
-
- json_value_free(object->values[i]);
- object->values[i] = NULL;
- }
- object->count = 0;
- for (i = 0; i < object->cell_capacity; i++) {
- object->cells[i] = OBJECT_INVALID_IX;
- }
- return JSONSuccess;
-}
-
-JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value) {
- JSON_Value *temp_schema_value = NULL, *temp_value = NULL;
- JSON_Array *schema_array = NULL, *value_array = NULL;
- JSON_Object *schema_object = NULL, *value_object = NULL;
- JSON_Value_Type schema_type = JSONError, value_type = JSONError;
- const char *key = NULL;
- size_t i = 0, count = 0;
- if (schema == NULL || value == NULL) {
- return JSONFailure;
- }
- schema_type = json_value_get_type(schema);
- value_type = json_value_get_type(value);
- if (schema_type != value_type && schema_type != JSONNull) { /* null represents all values */
- return JSONFailure;
- }
- switch (schema_type) {
- case JSONArray:
- schema_array = json_value_get_array(schema);
- value_array = json_value_get_array(value);
- count = json_array_get_count(schema_array);
- if (count == 0) {
- return JSONSuccess; /* Empty array allows all types */
- }
- /* Get first value from array, rest is ignored */
- temp_schema_value = json_array_get_value(schema_array, 0);
- for (i = 0; i < json_array_get_count(value_array); i++) {
- temp_value = json_array_get_value(value_array, i);
- if (json_validate(temp_schema_value, temp_value) != JSONSuccess) {
- return JSONFailure;
- }
- }
- return JSONSuccess;
- case JSONObject:
- schema_object = json_value_get_object(schema);
- value_object = json_value_get_object(value);
- count = json_object_get_count(schema_object);
- if (count == 0) {
- return JSONSuccess; /* Empty object allows all objects */
- } else if (json_object_get_count(value_object) < count) {
- return JSONFailure; /* Tested object mustn't have less name-value pairs than schema */
- }
- for (i = 0; i < count; i++) {
- key = json_object_get_name(schema_object, i);
- temp_schema_value = json_object_get_value(schema_object, key);
- temp_value = json_object_get_value(value_object, key);
- if (temp_value == NULL) {
- return JSONFailure;
- }
- if (json_validate(temp_schema_value, temp_value) != JSONSuccess) {
- return JSONFailure;
- }
- }
- return JSONSuccess;
- case JSONString: case JSONNumber: case JSONBoolean: case JSONNull:
- return JSONSuccess; /* equality already tested before switch */
- case JSONError: default:
- return JSONFailure;
- }
-}
-
-int json_value_equals(const JSON_Value *a, const JSON_Value *b) {
- JSON_Object *a_object = NULL, *b_object = NULL;
- JSON_Array *a_array = NULL, *b_array = NULL;
- const JSON_String *a_string = NULL, *b_string = NULL;
- const char *key = NULL;
- size_t a_count = 0, b_count = 0, i = 0;
- JSON_Value_Type a_type, b_type;
- a_type = json_value_get_type(a);
- b_type = json_value_get_type(b);
- if (a_type != b_type) {
- return PARSON_FALSE;
- }
- switch (a_type) {
- case JSONArray:
- a_array = json_value_get_array(a);
- b_array = json_value_get_array(b);
- a_count = json_array_get_count(a_array);
- b_count = json_array_get_count(b_array);
- if (a_count != b_count) {
- return PARSON_FALSE;
- }
- for (i = 0; i < a_count; i++) {
- if (!json_value_equals(json_array_get_value(a_array, i),
- json_array_get_value(b_array, i))) {
- return PARSON_FALSE;
- }
- }
- return PARSON_TRUE;
- case JSONObject:
- a_object = json_value_get_object(a);
- b_object = json_value_get_object(b);
- a_count = json_object_get_count(a_object);
- b_count = json_object_get_count(b_object);
- if (a_count != b_count) {
- return PARSON_FALSE;
- }
- for (i = 0; i < a_count; i++) {
- key = json_object_get_name(a_object, i);
- if (!json_value_equals(json_object_get_value(a_object, key),
- json_object_get_value(b_object, key))) {
- return PARSON_FALSE;
- }
- }
- return PARSON_TRUE;
- case JSONString:
- a_string = json_value_get_string_desc(a);
- b_string = json_value_get_string_desc(b);
- if (a_string == NULL || b_string == NULL) {
- return PARSON_FALSE; /* shouldn't happen */
- }
- return a_string->length == b_string->length &&
- memcmp(a_string->chars, b_string->chars, a_string->length) == 0;
- case JSONBoolean:
- return json_value_get_boolean(a) == json_value_get_boolean(b);
- case JSONNumber:
- return fabs(json_value_get_number(a) - json_value_get_number(b)) < 0.000001; /* EPSILON */
- case JSONError:
- return PARSON_TRUE;
- case JSONNull:
- return PARSON_TRUE;
- default:
- return PARSON_TRUE;
- }
-}
-
-JSON_Value_Type json_type(const JSON_Value *value) {
- return json_value_get_type(value);
-}
-
-JSON_Object * json_object (const JSON_Value *value) {
- return json_value_get_object(value);
-}
-
-JSON_Array * json_array(const JSON_Value *value) {
- return json_value_get_array(value);
-}
-
-const char * json_string(const JSON_Value *value) {
- return json_value_get_string(value);
-}
-
-size_t json_string_len(const JSON_Value *value) {
- return json_value_get_string_len(value);
-}
-
-double json_number(const JSON_Value *value) {
- return json_value_get_number(value);
-}
-
-int json_boolean(const JSON_Value *value) {
- return json_value_get_boolean(value);
-}
-
-void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun) {
- parson_malloc = malloc_fun;
- parson_free = free_fun;
-}
-
-void json_set_escape_slashes(int escape_slashes) {
- parson_escape_slashes = escape_slashes;
-}
-
-void json_set_float_serialization_format(const char *format) {
- if (parson_float_format) {
- parson_free(parson_float_format);
- parson_float_format = NULL;
- }
- if (!format) {
- parson_float_format = NULL;
- return;
- }
- parson_float_format = parson_strdup(format);
-}
-
-void json_set_number_serialization_function(JSON_Number_Serialization_Function func) {
- parson_number_serialization_function = func;
-}
diff --git a/parson.h b/parson.h
deleted file mode 100644
index 77b451ed93f3..000000000000
--- a/parson.h
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- SPDX-License-Identifier: MIT
-
- Parson 1.5.2 (https://github.com/kgabis/parson)
- Copyright (c) 2012 - 2023 Krzysztof Gabis
-
- 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 parson_parson_h
-#define parson_parson_h
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-#if 0
-} /* unconfuse xcode */
-#endif
-
-#define PARSON_VERSION_MAJOR 1
-#define PARSON_VERSION_MINOR 5
-#define PARSON_VERSION_PATCH 2
-
-#define PARSON_VERSION_STRING "1.5.2"
-
-#include <stddef.h> /* size_t */
-
-/* Types and enums */
-typedef struct json_object_t JSON_Object;
-typedef struct json_array_t JSON_Array;
-typedef struct json_value_t JSON_Value;
-
-enum json_value_type {
- JSONError = -1,
- JSONNull = 1,
- JSONString = 2,
- JSONNumber = 3,
- JSONObject = 4,
- JSONArray = 5,
- JSONBoolean = 6
-};
-typedef int JSON_Value_Type;
-
-enum json_result_t {
- JSONSuccess = 0,
- JSONFailure = -1
-};
-typedef int JSON_Status;
-
-typedef void * (*JSON_Malloc_Function)(size_t);
-typedef void (*JSON_Free_Function)(void *);
-
-/* A function used for serializing numbers (see json_set_number_serialization_function).
- If 'buf' is null then it should return number of bytes that would've been written
- (but not more than PARSON_NUM_BUF_SIZE).
-*/
-typedef int (*JSON_Number_Serialization_Function)(double num, char *buf);
-
-/* Call only once, before calling any other function from parson API. If not called, malloc and free
- from stdlib will be used for all allocations */
-void json_set_allocation_functions(JSON_Malloc_Function malloc_fun, JSON_Free_Function free_fun);
-
-/* Sets if slashes should be escaped or not when serializing JSON. By default slashes are escaped.
- This function sets a global setting and is not thread safe. */
-void json_set_escape_slashes(int escape_slashes);
-
-/* Sets float format used for serialization of numbers.
- Make sure it can't serialize to a string longer than PARSON_NUM_BUF_SIZE.
- If format is null then the default format is used. */
-void json_set_float_serialization_format(const char *format);
-
-/* Sets a function that will be used for serialization of numbers.
- If function is null then the default serialization function is used. */
-void json_set_number_serialization_function(JSON_Number_Serialization_Function fun);
-
-/* Parses first JSON value in a file, returns NULL in case of error */
-JSON_Value * json_parse_file(const char *filename);
-
-/* Parses first JSON value in a file and ignores comments (/ * * / and //),
- returns NULL in case of error */
-JSON_Value * json_parse_file_with_comments(const char *filename);
-
-/* Parses first JSON value in a string, returns NULL in case of error */
-JSON_Value * json_parse_string(const char *string);
-
-/* Parses first JSON value in a string and ignores comments (/ * * / and //),
- returns NULL in case of error */
-JSON_Value * json_parse_string_with_comments(const char *string);
-
-/* Serialization */
-size_t json_serialization_size(const JSON_Value *value); /* returns 0 on fail */
-JSON_Status json_serialize_to_buffer(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
-JSON_Status json_serialize_to_file(const JSON_Value *value, const char *filename);
-char * json_serialize_to_string(const JSON_Value *value);
-
-/* Pretty serialization */
-size_t json_serialization_size_pretty(const JSON_Value *value); /* returns 0 on fail */
-JSON_Status json_serialize_to_buffer_pretty(const JSON_Value *value, char *buf, size_t buf_size_in_bytes);
-JSON_Status json_serialize_to_file_pretty(const JSON_Value *value, const char *filename);
-char * json_serialize_to_string_pretty(const JSON_Value *value);
-
-void json_free_serialized_string(char *string); /* frees string from json_serialize_to_string and json_serialize_to_string_pretty */
-
-/* Comparing */
-int json_value_equals(const JSON_Value *a, const JSON_Value *b);
-
-/* Validation
- This is *NOT* JSON Schema. It validates json by checking if object have identically
- named fields with matching types.
- For example schema {"name":"", "age":0} will validate
- {"name":"Joe", "age":25} and {"name":"Joe", "age":25, "gender":"m"},
- but not {"name":"Joe"} or {"name":"Joe", "age":"Cucumber"}.
- In case of arrays, only first value in schema is checked against all values in tested array.
- Empty objects ({}) validate all objects, empty arrays ([]) validate all arrays,
- null validates values of every type.
- */
-JSON_Status json_validate(const JSON_Value *schema, const JSON_Value *value);
-
-/*
- * JSON Object
- */
-JSON_Value * json_object_get_value (const JSON_Object *object, const char *name);
-const char * json_object_get_string (const JSON_Object *object, const char *name);
-size_t json_object_get_string_len(const JSON_Object *object, const char *name); /* doesn't account for last null character */
-JSON_Object * json_object_get_object (const JSON_Object *object, const char *name);
-JSON_Array * json_object_get_array (const JSON_Object *object, const char *name);
-double json_object_get_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
-int json_object_get_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
-
-/* dotget functions enable addressing values with dot notation in nested objects,
- just like in structs or c++/java/c# objects (e.g. objectA.objectB.value).
- Because valid names in JSON can contain dots, some values may be inaccessible
- this way. */
-JSON_Value * json_object_dotget_value (const JSON_Object *object, const char *name);
-const char * json_object_dotget_string (const JSON_Object *object, const char *name);
-size_t json_object_dotget_string_len(const JSON_Object *object, const char *name); /* doesn't account for last null character */
-JSON_Object * json_object_dotget_object (const JSON_Object *object, const char *name);
-JSON_Array * json_object_dotget_array (const JSON_Object *object, const char *name);
-double json_object_dotget_number (const JSON_Object *object, const char *name); /* returns 0 on fail */
-int json_object_dotget_boolean(const JSON_Object *object, const char *name); /* returns -1 on fail */
-
-/* Functions to get available names */
-size_t json_object_get_count (const JSON_Object *object);
-const char * json_object_get_name (const JSON_Object *object, size_t index);
-JSON_Value * json_object_get_value_at(const JSON_Object *object, size_t index);
-JSON_Value * json_object_get_wrapping_value(const JSON_Object *object);
-
-/* Functions to check if object has a value with a specific name. Returned value is 1 if object has
- * a value and 0 if it doesn't. dothas functions behave exactly like dotget functions. */
-int json_object_has_value (const JSON_Object *object, const char *name);
-int json_object_has_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type);
-
-int json_object_dothas_value (const JSON_Object *object, const char *name);
-int json_object_dothas_value_of_type(const JSON_Object *object, const char *name, JSON_Value_Type type);
-
-/* Creates new name-value pair or frees and replaces old value with a new one.
- * json_object_set_value does not copy passed value so it shouldn't be freed afterwards. */
-JSON_Status json_object_set_value(JSON_Object *object, const char *name, JSON_Value *value);
-JSON_Status json_object_set_string(JSON_Object *object, const char *name, const char *string);
-JSON_Status json_object_set_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len); /* length shouldn't include last null character */
-JSON_Status json_object_set_number(JSON_Object *object, const char *name, double number);
-JSON_Status json_object_set_boolean(JSON_Object *object, const char *name, int boolean);
-JSON_Status json_object_set_null(JSON_Object *object, const char *name);
-
-/* Works like dotget functions, but creates whole hierarchy if necessary.
- * json_object_dotset_value does not copy passed value so it shouldn't be freed afterwards. */
-JSON_Status json_object_dotset_value(JSON_Object *object, const char *name, JSON_Value *value);
-JSON_Status json_object_dotset_string(JSON_Object *object, const char *name, const char *string);
-JSON_Status json_object_dotset_string_with_len(JSON_Object *object, const char *name, const char *string, size_t len); /* length shouldn't include last null character */
-JSON_Status json_object_dotset_number(JSON_Object *object, const char *name, double number);
-JSON_Status json_object_dotset_boolean(JSON_Object *object, const char *name, int boolean);
-JSON_Status json_object_dotset_null(JSON_Object *object, const char *name);
-
-/* Frees and removes name-value pair */
-JSON_Status json_object_remove(JSON_Object *object, const char *name);
-
-/* Works like dotget function, but removes name-value pair only on exact match. */
-JSON_Status json_object_dotremove(JSON_Object *object, const char *key);
-
-/* Removes all name-value pairs in object */
-JSON_Status json_object_clear(JSON_Object *object);
-
-/*
- *JSON Array
- */
-JSON_Value * json_array_get_value (const JSON_Array *array, size_t index);
-const char * json_array_get_string (const JSON_Array *array, size_t index);
-size_t json_array_get_string_len(const JSON_Array *array, size_t index); /* doesn't account for last null character */
-JSON_Object * json_array_get_object (const JSON_Array *array, size_t index);
-JSON_Array * json_array_get_array (const JSON_Array *array, size_t index);
-double json_array_get_number (const JSON_Array *array, size_t index); /* returns 0 on fail */
-int json_array_get_boolean(const JSON_Array *array, size_t index); /* returns -1 on fail */
-size_t json_array_get_count (const JSON_Array *array);
-JSON_Value * json_array_get_wrapping_value(const JSON_Array *array);
-
-/* Frees and removes value at given index, does nothing and returns JSONFailure if index doesn't exist.
- * Order of values in array may change during execution. */
-JSON_Status json_array_remove(JSON_Array *array, size_t i);
-
-/* Frees and removes from array value at given index and replaces it with given one.
- * Does nothing and returns JSONFailure if index doesn't exist.
- * json_array_replace_value does not copy passed value so it shouldn't be freed afterwards. */
-JSON_Status json_array_replace_value(JSON_Array *array, size_t i, JSON_Value *value);
-JSON_Status json_array_replace_string(JSON_Array *array, size_t i, const char* string);
-JSON_Status json_array_replace_string_with_len(JSON_Array *array, size_t i, const char *string, size_t len); /* length shouldn't include last null character */
-JSON_Status json_array_replace_number(JSON_Array *array, size_t i, double number);
-JSON_Status json_array_replace_boolean(JSON_Array *array, size_t i, int boolean);
-JSON_Status json_array_replace_null(JSON_Array *array, size_t i);
-
-/* Frees and removes all values from array */
-JSON_Status json_array_clear(JSON_Array *array);
-
-/* Appends new value at the end of array.
- * json_array_append_value does not copy passed value so it shouldn't be freed afterwards. */
-JSON_Status json_array_append_value(JSON_Array *array, JSON_Value *value);
-JSON_Status json_array_append_string(JSON_Array *array, const char *string);
-JSON_Status json_array_append_string_with_len(JSON_Array *array, const char *string, size_t len); /* length shouldn't include last null character */
-JSON_Status json_array_append_number(JSON_Array *array, double number);
-JSON_Status json_array_append_boolean(JSON_Array *array, int boolean);
-JSON_Status json_array_append_null(JSON_Array *array);
-
-/*
- *JSON Value
- */
-JSON_Value * json_value_init_object (void);
-JSON_Value * json_value_init_array (void);
-JSON_Value * json_value_init_string (const char *string); /* copies passed string */
-JSON_Value * json_value_init_string_with_len(const char *string, size_t length); /* copies passed string, length shouldn't include last null character */
-JSON_Value * json_value_init_number (double number);
-JSON_Value * json_value_init_boolean(int boolean);
-JSON_Value * json_value_init_null (void);
-JSON_Value * json_value_deep_copy (const JSON_Value *value);
-void json_value_free (JSON_Value *value);
-
-JSON_Value_Type json_value_get_type (const JSON_Value *value);
-JSON_Object * json_value_get_object (const JSON_Value *value);
-JSON_Array * json_value_get_array (const JSON_Value *value);
-const char * json_value_get_string (const JSON_Value *value);
-size_t json_value_get_string_len(const JSON_Value *value); /* doesn't account for last null character */
-double json_value_get_number (const JSON_Value *value);
-int json_value_get_boolean(const JSON_Value *value);
-JSON_Value * json_value_get_parent (const JSON_Value *value);
-
-/* Same as above, but shorter */
-JSON_Value_Type json_type (const JSON_Value *value);
-JSON_Object * json_object (const JSON_Value *value);
-JSON_Array * json_array (const JSON_Value *value);
-const char * json_string (const JSON_Value *value);
-size_t json_string_len(const JSON_Value *value); /* doesn't account for last null character */
-double json_number (const JSON_Value *value);
-int json_boolean(const JSON_Value *value);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/stb_truetype.h b/stb_truetype.h
deleted file mode 100644
index bbf2284b164c..000000000000
--- a/stb_truetype.h
+++ /dev/null
@@ -1,5077 +0,0 @@
-// stb_truetype.h - v1.26 - public domain
-// authored from 2009-2021 by Sean Barrett / RAD Game Tools
-//
-// =======================================================================
-//
-// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES
-//
-// This library does no range checking of the offsets found in the file,
-// meaning an attacker can use it to read arbitrary memory.
-//
-// =======================================================================
-//
-// This library processes TrueType files:
-// parse files
-// extract glyph metrics
-// extract glyph shapes
-// render glyphs to one-channel bitmaps with antialiasing (box filter)
-// render glyphs to one-channel SDF bitmaps (signed-distance field/function)
-//
-// Todo:
-// non-MS cmaps
-// crashproof on bad data
-// hinting? (no longer patented)
-// cleartype-style AA?
-// optimize: use simple memory allocator for intermediates
-// optimize: build edge-list directly from curves
-// optimize: rasterize directly from curves?
-//
-// ADDITIONAL CONTRIBUTORS
-//
-// Mikko Mononen: compound shape support, more cmap formats
-// Tor Andersson: kerning, subpixel rendering
-// Dougall Johnson: OpenType / Type 2 font handling
-// Daniel Ribeiro Maciel: basic GPOS-based kerning
-//
-// Misc other:
-// Ryan Gordon
-// Simon Glass
-// github:IntellectualKitty
-// Imanol Celaya
-// Daniel Ribeiro Maciel
-//
-// Bug/warning reports/fixes:
-// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe
-// Cass Everitt Martins Mozeiko github:aloucks
-// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam
-// Brian Hook Omar Cornut github:vassvik
-// Walter van Niftrik Ryan Griege
-// David Gow Peter LaValle
-// David Given Sergey Popov
-// Ivan-Assen Ivanov Giumo X. Clanjor
-// Anthony Pesch Higor Euripedes
-// Johan Duparc Thomas Fields
-// Hou Qiming Derek Vinyard
-// Rob Loach Cort Stratton
-// Kenney Phillis Jr. Brian Costabile
-// Ken Voskuil (kaesve)
-//
-// VERSION HISTORY
-//
-// 1.26 (2021-08-28) fix broken rasterizer
-// 1.25 (2021-07-11) many fixes
-// 1.24 (2020-02-05) fix warning
-// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
-// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
-// 1.21 (2019-02-25) fix warning
-// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()
-// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod
-// 1.18 (2018-01-29) add missing function
-// 1.17 (2017-07-23) make more arguments const; doc fix
-// 1.16 (2017-07-12) SDF support
-// 1.15 (2017-03-03) make more arguments const
-// 1.14 (2017-01-16) num-fonts-in-TTC function
-// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts
-// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
-// 1.11 (2016-04-02) fix unused-variable warning
-// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef
-// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly
-// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
-// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
-// variant PackFontRanges to pack and render in separate phases;
-// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
-// fixed an assert() bug in the new rasterizer
-// replace assert() with STBTT_assert() in new rasterizer
-//
-// Full history can be found at the end of this file.
-//
-// LICENSE
-//
-// See end of file for license information.
-//
-// USAGE
-//
-// Include this file in whatever places need to refer to it. In ONE C/C++
-// file, write:
-// #define STB_TRUETYPE_IMPLEMENTATION
-// before the #include of this file. This expands out the actual
-// implementation into that C/C++ file.
-//
-// To make the implementation private to the file that generates the implementation,
-// #define STBTT_STATIC
-//
-// Simple 3D API (don't ship this, but it's fine for tools and quick start)
-// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture
-// stbtt_GetBakedQuad() -- compute quad to draw for a given char
-//
-// Improved 3D API (more shippable):
-// #include "stb_rect_pack.h" -- optional, but you really want it
-// stbtt_PackBegin()
-// stbtt_PackSetOversampling() -- for improved quality on small fonts
-// stbtt_PackFontRanges() -- pack and renders
-// stbtt_PackEnd()
-// stbtt_GetPackedQuad()
-//
-// "Load" a font file from a memory buffer (you have to keep the buffer loaded)
-// stbtt_InitFont()
-// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections
-// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections
-//
-// Render a unicode codepoint to a bitmap
-// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
-// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
-// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
-//
-// Character advance/positioning
-// stbtt_GetCodepointHMetrics()
-// stbtt_GetFontVMetrics()
-// stbtt_GetFontVMetricsOS2()
-// stbtt_GetCodepointKernAdvance()
-//
-// Starting with version 1.06, the rasterizer was replaced with a new,
-// faster and generally-more-precise rasterizer. The new rasterizer more
-// accurately measures pixel coverage for anti-aliasing, except in the case
-// where multiple shapes overlap, in which case it overestimates the AA pixel
-// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If
-// this turns out to be a problem, you can re-enable the old rasterizer with
-// #define STBTT_RASTERIZER_VERSION 1
-// which will incur about a 15% speed hit.
-//
-// ADDITIONAL DOCUMENTATION
-//
-// Immediately after this block comment are a series of sample programs.
-//
-// After the sample programs is the "header file" section. This section
-// includes documentation for each API function.
-//
-// Some important concepts to understand to use this library:
-//
-// Codepoint
-// Characters are defined by unicode codepoints, e.g. 65 is
-// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is
-// the hiragana for "ma".
-//
-// Glyph
-// A visual character shape (every codepoint is rendered as
-// some glyph)
-//
-// Glyph index
-// A font-specific integer ID representing a glyph
-//
-// Baseline
-// Glyph shapes are defined relative to a baseline, which is the
-// bottom of uppercase characters. Characters extend both above
-// and below the baseline.
-//
-// Current Point
-// As you draw text to the screen, you keep track of a "current point"
-// which is the origin of each character. The current point's vertical
-// position is the baseline. Even "baked fonts" use this model.
-//
-// Vertical Font Metrics
-// The vertical qualities of the font, used to vertically position
-// and space the characters. See docs for stbtt_GetFontVMetrics.
-//
-// Font Size in Pixels or Points
-// The preferred interface for specifying font sizes in stb_truetype
-// is to specify how tall the font's vertical extent should be in pixels.
-// If that sounds good enough, skip the next paragraph.
-//
-// Most font APIs instead use "points", which are a common typographic
-// measurement for describing font size, defined as 72 points per inch.
-// stb_truetype provides a point API for compatibility. However, true
-// "per inch" conventions don't make much sense on computer displays
-// since different monitors have different number of pixels per
-// inch. For example, Windows traditionally uses a convention that
-// there are 96 pixels per inch, thus making 'inch' measurements have
-// nothing to do with inches, and thus effectively defining a point to
-// be 1.333 pixels. Additionally, the TrueType font data provides
-// an explicit scale factor to scale a given font's glyphs to points,
-// but the author has observed that this scale factor is often wrong
-// for non-commercial fonts, thus making fonts scaled in points
-// according to the TrueType spec incoherently sized in practice.
-//
-// DETAILED USAGE:
-//
-// Scale:
-// Select how high you want the font to be, in points or pixels.
-// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute
-// a scale factor SF that will be used by all other functions.
-//
-// Baseline:
-// You need to select a y-coordinate that is the baseline of where
-// your text will appear. Call GetFontBoundingBox to get the baseline-relative
-// bounding box for all characters. SF*-y0 will be the distance in pixels
-// that the worst-case character could extend above the baseline, so if
-// you want the top edge of characters to appear at the top of the
-// screen where y=0, then you would set the baseline to SF*-y0.
-//
-// Current point:
-// Set the current point where the first character will appear. The
-// first character could extend left of the current point; this is font
-// dependent. You can either choose a current point that is the leftmost
-// point and hope, or add some padding, or check the bounding box or
-// left-side-bearing of the first character to be displayed and set
-// the current point based on that.
-//
-// Displaying a character:
-// Compute the bounding box of the character. It will contain signed values
-// relative to <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,
-// then the character should be displayed in the rectangle from
-// <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).
-//
-// Advancing for the next character:
-// Call GlyphHMetrics, and compute 'current_point += SF * advance'.
-//
-//
-// ADVANCED USAGE
-//
-// Quality:
-//
-// - Use the functions with Subpixel at the end to allow your characters
-// to have subpixel positioning. Since the font is anti-aliased, not
-// hinted, this is very import for quality. (This is not possible with
-// baked fonts.)
-//
-// - Kerning is now supported, and if you're supporting subpixel rendering
-// then kerning is worth using to give your text a polished look.
-//
-// Performance:
-//
-// - Convert Unicode codepoints to glyph indexes and operate on the glyphs;
-// if you don't do this, stb_truetype is forced to do the conversion on
-// every call.
-//
-// - There are a lot of memory allocations. We should modify it to take
-// a temp buffer and allocate from the temp buffer (without freeing),
-// should help performance a lot.
-//
-// NOTES
-//
-// The system uses the raw data found in the .ttf file without changing it
-// and without building auxiliary data structures. This is a bit inefficient
-// on little-endian systems (the data is big-endian), but assuming you're
-// caching the bitmaps or glyph shapes this shouldn't be a big deal.
-//
-// It appears to be very hard to programmatically determine what font a
-// given file is in a general way. I provide an API for this, but I don't
-// recommend it.
-//
-//
-// PERFORMANCE MEASUREMENTS FOR 1.06:
-//
-// 32-bit 64-bit
-// Previous release: 8.83 s 7.68 s
-// Pool allocations: 7.72 s 6.34 s
-// Inline sort : 6.54 s 5.65 s
-// New rasterizer : 5.63 s 5.00 s
-
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
-////
-//// SAMPLE PROGRAMS
-////
-//
-// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless.
-// See "tests/truetype_demo_win32.c" for a complete version.
-#if 0
-#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
-#include "stb_truetype.h"
-
-unsigned char ttf_buffer[1<<20];
-unsigned char temp_bitmap[512*512];
-
-stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs
-GLuint ftex;
-
-void my_stbtt_initfont(void)
-{
- fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));
- stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!
- // can free ttf_buffer at this point
- glGenTextures(1, &ftex);
- glBindTexture(GL_TEXTURE_2D, ftex);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
- // can free temp_bitmap at this point
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-}
-
-void my_stbtt_print(float x, float y, char *text)
-{
- // assume orthographic projection with units = screen pixels, origin at top left
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, ftex);
- glBegin(GL_QUADS);
- while (*text) {
- if (*text >= 32 && *text < 128) {
- stbtt_aligned_quad q;
- stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
- glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0);
- glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0);
- glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1);
- glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1);
- }
- ++text;
- }
- glEnd();
-}
-#endif
-//
-//
-//////////////////////////////////////////////////////////////////////////////
-//
-// Complete program (this compiles): get a single bitmap, print as ASCII art
-//
-#if 0
-#include <stdio.h>
-#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
-#include "stb_truetype.h"
-
-char ttf_buffer[1<<25];
-
-int main(int argc, char **argv)
-{
- stbtt_fontinfo font;
- unsigned char *bitmap;
- int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);
-
- fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb"));
-
- stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
- bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);
-
- for (j=0; j < h; ++j) {
- for (i=0; i < w; ++i)
- putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
- putchar('\n');
- }
- return 0;
-}
-#endif
-//
-// Output:
-//
-// .ii.
-// @@@@@@.
-// V@Mio@@o
-// :i. V@V
-// :oM@@M
-// :@@@MM@M
-// @@o o@M
-// :@@. M@M
-// @@@o@@@@
-// :M@@V:@@.
-//
-//////////////////////////////////////////////////////////////////////////////
-//
-// Complete program: print "Hello World!" banner, with bugs
-//
-#if 0
-char buffer[24<<20];
-unsigned char screen[20][79];
-
-int main(int arg, char **argv)
-{
- stbtt_fontinfo font;
- int i,j,ascent,baseline,ch=0;
- float scale, xpos=2; // leave a little padding in case the character extends left
- char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness
-
- fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
- stbtt_InitFont(&font, buffer, 0);
-
- scale = stbtt_ScaleForPixelHeight(&font, 15);
- stbtt_GetFontVMetrics(&font, &ascent,0,0);
- baseline = (int) (ascent*scale);
-
- while (text[ch]) {
- int advance,lsb,x0,y0,x1,y1;
- float x_shift = xpos - (float) floor(xpos);
- stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);
- stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);
- stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);
- // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong
- // because this API is really for baking character bitmaps into textures. if you want to render
- // a sequence of characters, you really need to render each bitmap to a temp buffer, then
- // "alpha blend" that into the working buffer
- xpos += (advance * scale);
- if (text[ch+1])
- xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);
- ++ch;
- }
-
- for (j=0; j < 20; ++j) {
- for (i=0; i < 78; ++i)
- putchar(" .:ioVM@"[screen[j][i]>>5]);
- putchar('\n');
- }
-
- return 0;
-}
-#endif
-
-
-//////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////
-////
-//// INTEGRATION WITH YOUR CODEBASE
-////
-//// The following sections allow you to supply alternate definitions
-//// of C library functions used by stb_truetype, e.g. if you don't
-//// link with the C runtime library.
-
-#ifdef STB_TRUETYPE_IMPLEMENTATION
- // #define your own (u)stbtt_int8/16/32 before including to override this
- #ifndef stbtt_uint8
- typedef unsigned char stbtt_uint8;
- typedef signed char stbtt_int8;
- typedef unsigned short stbtt_uint16;
- typedef signed short stbtt_int16;
- typedef unsigned int stbtt_uint32;
- typedef signed int stbtt_int32;
- #endif
-
- typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
- typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
-
- // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
- #ifndef STBTT_ifloor
- #include <math.h>
- #define STBTT_ifloor(x) ((int) floor(x))
- #define STBTT_iceil(x) ((int) ceil(x))
- #endif
-
- #ifndef STBTT_sqrt
- #include <math.h>
- #define STBTT_sqrt(x) sqrt(x)
- #define STBTT_pow(x,y) pow(x,y)
- #endif
-
- #ifndef STBTT_fmod
- #include <math.h>
- #define STBTT_fmod(x,y) fmod(x,y)
- #endif
-
- #ifndef STBTT_cos
- #include <math.h>
- #define STBTT_cos(x) cos(x)
- #define STBTT_acos(x) acos(x)
- #endif
-
- #ifndef STBTT_fabs
- #include <math.h>
- #define STBTT_fabs(x) fabs(x)
- #endif
-
- // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h
- #ifndef STBTT_malloc
- #include <stdlib.h>
- #define STBTT_malloc(x,u) ((void)(u),malloc(x))
- #define STBTT_free(x,u) ((void)(u),free(x))
- #endif
-
- #ifndef STBTT_assert
- #include <assert.h>
- #define STBTT_assert(x) assert(x)
- #endif
-
- #ifndef STBTT_strlen
- #include <string.h>
- #define STBTT_strlen(x) strlen(x)
- #endif
-
- #ifndef STBTT_memcpy
- #include <string.h>
- #define STBTT_memcpy memcpy
- #define STBTT_memset memset
- #endif
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-////
-//// INTERFACE
-////
-////
-
-#ifndef __STB_INCLUDE_STB_TRUETYPE_H__
-#define __STB_INCLUDE_STB_TRUETYPE_H__
-
-#ifdef STBTT_STATIC
-#define STBTT_DEF static
-#else
-#define STBTT_DEF extern
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// private structure
-typedef struct
-{
- unsigned char *data;
- int cursor;
- int size;
-} stbtt__buf;
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// TEXTURE BAKING API
-//
-// If you use this API, you only have to call two functions ever.
-//
-
-typedef struct
-{
- unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
- float xoff,yoff,xadvance;
-} stbtt_bakedchar;
-
-STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
- float pixel_height, // height of font in pixels
- unsigned char *pixels, int pw, int ph, // bitmap to be filled in
- int first_char, int num_chars, // characters to bake
- stbtt_bakedchar *chardata); // you allocate this, it's num_chars long
-// if return is positive, the first unused row of the bitmap
-// if return is negative, returns the negative of the number of characters that fit
-// if return is 0, no characters fit and no rows were used
-// This uses a very crappy packing.
-
-typedef struct
-{
- float x0,y0,s0,t0; // top-left
- float x1,y1,s1,t1; // bottom-right
-} stbtt_aligned_quad;
-
-STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above
- int char_index, // character to display
- float *xpos, float *ypos, // pointers to current position in screen pixel space
- stbtt_aligned_quad *q, // output: quad to draw
- int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier
-// Call GetBakedQuad with char_index = 'character - first_char', and it
-// creates the quad you need to draw and advances the current position.
-//
-// The coordinate system used assumes y increases downwards.
-//
-// Characters will extend both above and below the current position;
-// see discussion of "BASELINE" above.
-//
-// It's inefficient; you might want to c&p it and optimize it.
-
-STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap);
-// Query the font vertical metrics without having to create a font first.
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// NEW TEXTURE BAKING API
-//
-// This provides options for packing multiple fonts into one atlas, not
-// perfectly but better than nothing.
-
-typedef struct
-{
- unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
- float xoff,yoff,xadvance;
- float xoff2,yoff2;
-} stbtt_packedchar;
-
-typedef struct stbtt_pack_context stbtt_pack_context;
-typedef struct stbtt_fontinfo stbtt_fontinfo;
-#ifndef STB_RECT_PACK_VERSION
-typedef struct stbrp_rect stbrp_rect;
-#endif
-
-STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);
-// Initializes a packing context stored in the passed-in stbtt_pack_context.
-// Future calls using this context will pack characters into the bitmap passed
-// in here: a 1-channel bitmap that is width * height. stride_in_bytes is
-// the distance from one row to the next (or 0 to mean they are packed tightly
-// together). "padding" is the amount of padding to leave between each
-// character (normally you want '1' for bitmaps you'll use as textures with
-// bilinear filtering).
-//
-// Returns 0 on failure, 1 on success.
-
-STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc);
-// Cleans up the packing context and frees all memory.
-
-#define STBTT_POINT_SIZE(x) (-(x))
-
-STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,
- int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);
-// Creates character bitmaps from the font_index'th font found in fontdata (use
-// font_index=0 if you don't know what that is). It creates num_chars_in_range
-// bitmaps for characters with unicode values starting at first_unicode_char_in_range
-// and increasing. Data for how to render them is stored in chardata_for_range;
-// pass these to stbtt_GetPackedQuad to get back renderable quads.
-//
-// font_size is the full height of the character from ascender to descender,
-// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed
-// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE()
-// and pass that result as 'font_size':
-// ..., 20 , ... // font max minus min y is 20 pixels tall
-// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall
-
-typedef struct
-{
- float font_size;
- int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint
- int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints
- int num_chars;
- stbtt_packedchar *chardata_for_range; // output
- unsigned char h_oversample, v_oversample; // don't set these, they're used internally
-} stbtt_pack_range;
-
-STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);
-// Creates character bitmaps from multiple ranges of characters stored in
-// ranges. This will usually create a better-packed bitmap than multiple
-// calls to stbtt_PackFontRange. Note that you can call this multiple
-// times within a single PackBegin/PackEnd.
-
-STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);
-// Oversampling a font increases the quality by allowing higher-quality subpixel
-// positioning, and is especially valuable at smaller text sizes.
-//
-// This function sets the amount of oversampling for all following calls to
-// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given
-// pack context. The default (no oversampling) is achieved by h_oversample=1
-// and v_oversample=1. The total number of pixels required is
-// h_oversample*v_oversample larger than the default; for example, 2x2
-// oversampling requires 4x the storage of 1x1. For best results, render
-// oversampled textures with bilinear filtering. Look at the readme in
-// stb/tests/oversample for information about oversampled fonts
-//
-// To use with PackFontRangesGather etc., you must set it before calls
-// call to PackFontRangesGatherRects.
-
-STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);
-// If skip != 0, this tells stb_truetype to skip any codepoints for which
-// there is no corresponding glyph. If skip=0, which is the default, then
-// codepoints without a glyph recived the font's "missing character" glyph,
-// typically an empty box by convention.
-
-STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above
- int char_index, // character to display
- float *xpos, float *ypos, // pointers to current position in screen pixel space
- stbtt_aligned_quad *q, // output: quad to draw
- int align_to_integer);
-
-STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
-STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects);
-STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
-// Calling these functions in sequence is roughly equivalent to calling
-// stbtt_PackFontRanges(). If you more control over the packing of multiple
-// fonts, or if you want to pack custom data into a font texture, take a look
-// at the source to of stbtt_PackFontRanges() and create a custom version
-// using these functions, e.g. call GatherRects multiple times,
-// building up a single array of rects, then call PackRects once,
-// then call RenderIntoRects repeatedly. This may result in a
-// better packing than calling PackFontRanges multiple times
-// (or it may not).
-
-// this is an opaque structure that you shouldn't mess with which holds
-// all the context needed from PackBegin to PackEnd.
-struct stbtt_pack_context {
- void *user_allocator_context;
- void *pack_info;
- int width;
- int height;
- int stride_in_bytes;
- int padding;
- int skip_missing;
- unsigned int h_oversample, v_oversample;
- unsigned char *pixels;
- void *nodes;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// FONT LOADING
-//
-//
-
-STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data);
-// This function will determine the number of fonts in a font file. TrueType
-// collection (.ttc) files may contain multiple fonts, while TrueType font
-// (.ttf) files only contain one font. The number of fonts can be used for
-// indexing with the previous function where the index is between zero and one
-// less than the total fonts. If an error occurs, -1 is returned.
-
-STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
-// Each .ttf/.ttc file may have more than one font. Each font has a sequential
-// index number starting from 0. Call this function to get the font offset for
-// a given index; it returns -1 if the index is out of range. A regular .ttf
-// file will only define one font and it always be at offset 0, so it will
-// return '0' for index 0, and -1 for all other indices.
-
-// The following structure is defined publicly so you can declare one on
-// the stack or as a global or etc, but you should treat it as opaque.
-struct stbtt_fontinfo
-{
- void * userdata;
- unsigned char * data; // pointer to .ttf file
- int fontstart; // offset of start of font
-
- int numGlyphs; // number of glyphs, needed for range checking
-
- int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf
- int index_map; // a cmap mapping for our chosen character encoding
- int indexToLocFormat; // format needed to map from glyph index to glyph
-
- stbtt__buf cff; // cff font data
- stbtt__buf charstrings; // the charstring index
- stbtt__buf gsubrs; // global charstring subroutines index
- stbtt__buf subrs; // private charstring subroutines index
- stbtt__buf fontdicts; // array of font dicts
- stbtt__buf fdselect; // map from glyph to fontdict
-};
-
-STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
-// Given an offset into the file that defines a font, this function builds
-// the necessary cached info for the rest of the system. You must allocate
-// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
-// need to do anything special to free it, because the contents are pure
-// value data with no additional data structures. Returns 0 on failure.
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// CHARACTER TO GLYPH-INDEX CONVERSIOn
-
-STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
-// If you're going to perform multiple operations on the same character
-// and you want a speed-up, call this function with the character you're
-// going to process, then use glyph-based functions instead of the
-// codepoint-based functions.
-// Returns 0 if the character codepoint is not defined in the font.
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// CHARACTER PROPERTIES
-//
-
-STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);
-// computes a scale factor to produce a font whose "height" is 'pixels' tall.
-// Height is measured as the distance from the highest ascender to the lowest
-// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
-// and computing:
-// scale = pixels / (ascent - descent)
-// so if you prefer to measure height by the ascent only, use a similar calculation.
-
-STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);
-// computes a scale factor to produce a font whose EM size is mapped to
-// 'pixels' tall. This is probably what traditional APIs compute, but
-// I'm not positive.
-
-STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);
-// ascent is the coordinate above the baseline the font extends; descent
-// is the coordinate below the baseline the font extends (i.e. it is typically negative)
-// lineGap is the spacing between one row's descent and the next row's ascent...
-// so you should advance the vertical position by "*ascent - *descent + *lineGap"
-// these are expressed in unscaled coordinates, so you must multiply by
-// the scale factor for a given size
-
-STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap);
-// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2
-// table (specific to MS/Windows TTF files).
-//
-// Returns 1 on success (table present), 0 on failure.
-
-STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);
-// the bounding box around all possible characters
-
-STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
-// leftSideBearing is the offset from the current horizontal position to the left edge of the character
-// advanceWidth is the offset from the current horizontal position to the next horizontal position
-// these are expressed in unscaled coordinates
-
-STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
-// an additional amount to add to the 'advance' value between ch1 and ch2
-
-STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
-// Gets the bounding box of the visible part of the glyph, in unscaled coordinates
-
-STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);
-STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);
-STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
-// as above, but takes one or more glyph indices for greater efficiency
-
-typedef struct stbtt_kerningentry
-{
- int glyph1; // use stbtt_FindGlyphIndex
- int glyph2;
- int advance;
-} stbtt_kerningentry;
-
-STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info);
-STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length);
-// Retrieves a complete list of all of the kerning pairs provided by the font
-// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write.
-// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1)
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// GLYPH SHAPES (you probably don't need these, but they have to go before
-// the bitmaps for C declaration-order reasons)
-//
-
-#ifndef STBTT_vmove // you can predefine these to use different values (but why?)
- enum {
- STBTT_vmove=1,
- STBTT_vline,
- STBTT_vcurve,
- STBTT_vcubic
- };
-#endif
-
-#ifndef stbtt_vertex // you can predefine this to use different values
- // (we share this with other code at RAD)
- #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file
- typedef struct
- {
- stbtt_vertex_type x,y,cx,cy,cx1,cy1;
- unsigned char type,padding;
- } stbtt_vertex;
-#endif
-
-STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);
-// returns non-zero if nothing is drawn for this glyph
-
-STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);
-STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);
-// returns # of vertices and fills *vertices with the pointer to them
-// these are expressed in "unscaled" coordinates
-//
-// The shape is a series of contours. Each one starts with
-// a STBTT_moveto, then consists of a series of mixed
-// STBTT_lineto and STBTT_curveto segments. A lineto
-// draws a line from previous endpoint to its x,y; a curveto
-// draws a quadratic bezier from previous endpoint to
-// its x,y, using cx,cy as the bezier control point.
-
-STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
-// frees the data allocated above
-
-STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl);
-STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg);
-STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg);
-// fills svg with the character's SVG data.
-// returns data size or 0 if SVG not found.
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// BITMAP RENDERING
-//
-
-STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);
-// frees the bitmap allocated below
-
-STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
-// allocates a large-enough single-channel 8bpp bitmap and renders the
-// specified character/glyph at the specified scale into it, with
-// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
-// *width & *height are filled out with the width & height of the bitmap,
-// which is stored left-to-right, top-to-bottom.
-//
-// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
-
-STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
-// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel
-// shift for the character
-
-STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
-// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap
-// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap
-// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the
-// width and height and positioning info for it first.
-
-STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);
-// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel
-// shift for the character
-
-STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint);
-// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering
-// is performed (see stbtt_PackSetOversampling)
-
-STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
-// get the bbox of the bitmap centered around the glyph origin; so the
-// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
-// the bitmap top left is (leftSideBearing*scale,iy0).
-// (Note that the bitmap uses y-increases-down, but the shape uses
-// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
-
-STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
-// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel
-// shift for the character
-
-// the following functions are equivalent to the above functions, but operate
-// on glyph indices instead of Unicode codepoints (for efficiency)
-STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);
-STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);
-STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
-STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);
-STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph);
-STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
-STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
-
-
-// @TODO: don't expose this structure
-typedef struct
-{
- int w,h,stride;
- unsigned char *pixels;
-} stbtt__bitmap;
-
-// rasterize a shape with quadratic beziers into a bitmap
-STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into
- float flatness_in_pixels, // allowable error of curve in pixels
- stbtt_vertex *vertices, // array of vertices defining shape
- int num_verts, // number of vertices in above array
- float scale_x, float scale_y, // scale applied to input vertices
- float shift_x, float shift_y, // translation applied to input vertices
- int x_off, int y_off, // another translation applied to input
- int invert, // if non-zero, vertically flip shape
- void *userdata); // context for to STBTT_MALLOC
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Signed Distance Function (or Field) rendering
-
-STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata);
-// frees the SDF bitmap allocated below
-
-STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
-STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff);
-// These functions compute a discretized SDF field for a single character, suitable for storing
-// in a single-channel texture, sampling with bilinear filtering, and testing against
-// larger than some threshold to produce scalable fonts.
-// info -- the font
-// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap
-// glyph/codepoint -- the character to generate the SDF for
-// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0),
-// which allows effects like bit outlines
-// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character)
-// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale)
-// if positive, > onedge_value is inside; if negative, < onedge_value is inside
-// width,height -- output height & width of the SDF bitmap (including padding)
-// xoff,yoff -- output origin of the character
-// return value -- a 2D array of bytes 0..255, width*height in size
-//
-// pixel_dist_scale & onedge_value are a scale & bias that allows you to make
-// optimal use of the limited 0..255 for your application, trading off precision
-// and special effects. SDF values outside the range 0..255 are clamped to 0..255.
-//
-// Example:
-// scale = stbtt_ScaleForPixelHeight(22)
-// padding = 5
-// onedge_value = 180
-// pixel_dist_scale = 180/5.0 = 36.0
-//
-// This will create an SDF bitmap in which the character is about 22 pixels
-// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled
-// shape, sample the SDF at each pixel and fill the pixel if the SDF value
-// is greater than or equal to 180/255. (You'll actually want to antialias,
-// which is beyond the scope of this example.) Additionally, you can compute
-// offset outlines (e.g. to stroke the character border inside & outside,
-// or only outside). For example, to fill outside the character up to 3 SDF
-// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above
-// choice of variables maps a range from 5 pixels outside the shape to
-// 2 pixels inside the shape to 0..255; this is intended primarily for apply
-// outside effects only (the interior range is needed to allow proper
-// antialiasing of the font at *smaller* sizes)
-//
-// The function computes the SDF analytically at each SDF pixel, not by e.g.
-// building a higher-res bitmap and approximating it. In theory the quality
-// should be as high as possible for an SDF of this size & representation, but
-// unclear if this is true in practice (perhaps building a higher-res bitmap
-// and computing from that can allow drop-out prevention).
-//
-// The algorithm has not been optimized at all, so expect it to be slow
-// if computing lots of characters or very large sizes.
-
-
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Finding the right font...
-//
-// You should really just solve this offline, keep your own tables
-// of what font is what, and don't try to get it out of the .ttf file.
-// That's because getting it out of the .ttf file is really hard, because
-// the names in the file can appear in many possible encodings, in many
-// possible languages, and e.g. if you need a case-insensitive comparison,
-// the details of that depend on the encoding & language in a complex way
-// (actually underspecified in truetype, but also gigantic).
-//
-// But you can use the provided functions in two possible ways:
-// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on
-// unicode-encoded names to try to find the font you want;
-// you can run this before calling stbtt_InitFont()
-//
-// stbtt_GetFontNameString() lets you get any of the various strings
-// from the file yourself and do your own comparisons on them.
-// You have to have called stbtt_InitFont() first.
-
-
-STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);
-// returns the offset (not index) of the font that matches, or -1 if none
-// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".
-// if you use any other flag, use a font name like "Arial"; this checks
-// the 'macStyle' header field; i don't know if fonts set this consistently
-#define STBTT_MACSTYLE_DONTCARE 0
-#define STBTT_MACSTYLE_BOLD 1
-#define STBTT_MACSTYLE_ITALIC 2
-#define STBTT_MACSTYLE_UNDERSCORE 4
-#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0
-
-STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);
-// returns 1/0 whether the first string interpreted as utf8 is identical to
-// the second string interpreted as big-endian utf16... useful for strings from next func
-
-STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);
-// returns the string (which may be big-endian double byte, e.g. for unicode)
-// and puts the length in bytes in *length.
-//
-// some of the values for the IDs are below; for more see the truetype spec:
-// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
-// http://www.microsoft.com/typography/otspec/name.htm
-
-enum { // platformID
- STBTT_PLATFORM_ID_UNICODE =0,
- STBTT_PLATFORM_ID_MAC =1,
- STBTT_PLATFORM_ID_ISO =2,
- STBTT_PLATFORM_ID_MICROSOFT =3
-};
-
-enum { // encodingID for STBTT_PLATFORM_ID_UNICODE
- STBTT_UNICODE_EID_UNICODE_1_0 =0,
- STBTT_UNICODE_EID_UNICODE_1_1 =1,
- STBTT_UNICODE_EID_ISO_10646 =2,
- STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,
- STBTT_UNICODE_EID_UNICODE_2_0_FULL=4
-};
-
-enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT
- STBTT_MS_EID_SYMBOL =0,
- STBTT_MS_EID_UNICODE_BMP =1,
- STBTT_MS_EID_SHIFTJIS =2,
- STBTT_MS_EID_UNICODE_FULL =10
-};
-
-enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes
- STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4,
- STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5,
- STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6,
- STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7
-};
-
-enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...
- // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs
- STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410,
- STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411,
- STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412,
- STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419,
- STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409,
- STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D
-};
-
-enum { // languageID for STBTT_PLATFORM_ID_MAC
- STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11,
- STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23,
- STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32,
- STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 ,
- STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 ,
- STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,
- STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // __STB_INCLUDE_STB_TRUETYPE_H__
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-////
-//// IMPLEMENTATION
-////
-////
-
-#ifdef STB_TRUETYPE_IMPLEMENTATION
-
-#ifndef STBTT_MAX_OVERSAMPLE
-#define STBTT_MAX_OVERSAMPLE 8
-#endif
-
-#if STBTT_MAX_OVERSAMPLE > 255
-#error "STBTT_MAX_OVERSAMPLE cannot be > 255"
-#endif
-
-typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1];
-
-#ifndef STBTT_RASTERIZER_VERSION
-#define STBTT_RASTERIZER_VERSION 2
-#endif
-
-#ifdef _MSC_VER
-#define STBTT__NOTUSED(v) (void)(v)
-#else
-#define STBTT__NOTUSED(v) (void)sizeof(v)
-#endif
-
-//////////////////////////////////////////////////////////////////////////
-//
-// stbtt__buf helpers to parse data from file
-//
-
-static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b)
-{
- if (b->cursor >= b->size)
- return 0;
- return b->data[b->cursor++];
-}
-
-static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b)
-{
- if (b->cursor >= b->size)
- return 0;
- return b->data[b->cursor];
-}
-
-static void stbtt__buf_seek(stbtt__buf *b, int o)
-{
- STBTT_assert(!(o > b->size || o < 0));
- b->cursor = (o > b->size || o < 0) ? b->size : o;
-}
-
-static void stbtt__buf_skip(stbtt__buf *b, int o)
-{
- stbtt__buf_seek(b, b->cursor + o);
-}
-
-static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n)
-{
- stbtt_uint32 v = 0;
- int i;
- STBTT_assert(n >= 1 && n <= 4);
- for (i = 0; i < n; i++)
- v = (v << 8) | stbtt__buf_get8(b);
- return v;
-}
-
-static stbtt__buf stbtt__new_buf(const void *p, size_t size)
-{
- stbtt__buf r;
- STBTT_assert(size < 0x40000000);
- r.data = (stbtt_uint8*) p;
- r.size = (int) size;
- r.cursor = 0;
- return r;
-}
-
-#define stbtt__buf_get16(b) stbtt__buf_get((b), 2)
-#define stbtt__buf_get32(b) stbtt__buf_get((b), 4)
-
-static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s)
-{
- stbtt__buf r = stbtt__new_buf(NULL, 0);
- if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r;
- r.data = b->data + o;
- r.size = s;
- return r;
-}
-
-static stbtt__buf stbtt__cff_get_index(stbtt__buf *b)
-{
- int count, start, offsize;
- start = b->cursor;
- count = stbtt__buf_get16(b);
- if (count) {
- offsize = stbtt__buf_get8(b);
- STBTT_assert(offsize >= 1 && offsize <= 4);
- stbtt__buf_skip(b, offsize * count);
- stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1);
- }
- return stbtt__buf_range(b, start, b->cursor - start);
-}
-
-static stbtt_uint32 stbtt__cff_int(stbtt__buf *b)
-{
- int b0 = stbtt__buf_get8(b);
- if (b0 >= 32 && b0 <= 246) return b0 - 139;
- else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108;
- else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108;
- else if (b0 == 28) return stbtt__buf_get16(b);
- else if (b0 == 29) return stbtt__buf_get32(b);
- STBTT_assert(0);
- return 0;
-}
-
-static void stbtt__cff_skip_operand(stbtt__buf *b) {
- int v, b0 = stbtt__buf_peek8(b);
- STBTT_assert(b0 >= 28);
- if (b0 == 30) {
- stbtt__buf_skip(b, 1);
- while (b->cursor < b->size) {
- v = stbtt__buf_get8(b);
- if ((v & 0xF) == 0xF || (v >> 4) == 0xF)
- break;
- }
- } else {
- stbtt__cff_int(b);
- }
-}
-
-static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key)
-{
- stbtt__buf_seek(b, 0);
- while (b->cursor < b->size) {
- int start = b->cursor, end, op;
- while (stbtt__buf_peek8(b) >= 28)
- stbtt__cff_skip_operand(b);
- end = b->cursor;
- op = stbtt__buf_get8(b);
- if (op == 12) op = stbtt__buf_get8(b) | 0x100;
- if (op == key) return stbtt__buf_range(b, start, end-start);
- }
- return stbtt__buf_range(b, 0, 0);
-}
-
-static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out)
-{
- int i;
- stbtt__buf operands = stbtt__dict_get(b, key);
- for (i = 0; i < outcount && operands.cursor < operands.size; i++)
- out[i] = stbtt__cff_int(&operands);
-}
-
-static int stbtt__cff_index_count(stbtt__buf *b)
-{
- stbtt__buf_seek(b, 0);
- return stbtt__buf_get16(b);
-}
-
-static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i)
-{
- int count, offsize, start, end;
- stbtt__buf_seek(&b, 0);
- count = stbtt__buf_get16(&b);
- offsize = stbtt__buf_get8(&b);
- STBTT_assert(i >= 0 && i < count);
- STBTT_assert(offsize >= 1 && offsize <= 4);
- stbtt__buf_skip(&b, i*offsize);
- start = stbtt__buf_get(&b, offsize);
- end = stbtt__buf_get(&b, offsize);
- return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start);
-}
-
-//////////////////////////////////////////////////////////////////////////
-//
-// accessors to parse data from file
-//
-
-// on platforms that don't allow misaligned reads, if we want to allow
-// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE
-
-#define ttBYTE(p) (* (stbtt_uint8 *) (p))
-#define ttCHAR(p) (* (stbtt_int8 *) (p))
-#define ttFixed(p) ttLONG(p)
-
-static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; }
-static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; }
-static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
-static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
-
-#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
-#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3])
-
-static int stbtt__isfont(stbtt_uint8 *font)
-{
- // check the version number
- if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1
- if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this!
- if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF
- if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0
- if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts
- return 0;
-}
-
-// @OPTIMIZE: binary search
-static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)
-{
- stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);
- stbtt_uint32 tabledir = fontstart + 12;
- stbtt_int32 i;
- for (i=0; i < num_tables; ++i) {
- stbtt_uint32 loc = tabledir + 16*i;
- if (stbtt_tag(data+loc+0, tag))
- return ttULONG(data+loc+8);
- }
- return 0;
-}
-
-static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index)
-{
- // if it's just a font, there's only one valid index
- if (stbtt__isfont(font_collection))
- return index == 0 ? 0 : -1;
-
- // check if it's a TTC
- if (stbtt_tag(font_collection, "ttcf")) {
- // version 1?
- if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
- stbtt_int32 n = ttLONG(font_collection+8);
- if (index >= n)
- return -1;
- return ttULONG(font_collection+12+index*4);
- }
- }
- return -1;
-}
-
-static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection)
-{
- // if it's just a font, there's only one valid font
- if (stbtt__isfont(font_collection))
- return 1;
-
- // check if it's a TTC
- if (stbtt_tag(font_collection, "ttcf")) {
- // version 1?
- if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
- return ttLONG(font_collection+8);
- }
- }
- return 0;
-}
-
-static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict)
-{
- stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 };
- stbtt__buf pdict;
- stbtt__dict_get_ints(&fontdict, 18, 2, private_loc);
- if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0);
- pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]);
- stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff);
- if (!subrsoff) return stbtt__new_buf(NULL, 0);
- stbtt__buf_seek(&cff, private_loc[1]+subrsoff);
- return stbtt__cff_get_index(&cff);
-}
-
-// since most people won't use this, find this table the first time it's needed
-static int stbtt__get_svg(stbtt_fontinfo *info)
-{
- stbtt_uint32 t;
- if (info->svg < 0) {
- t = stbtt__find_table(info->data, info->fontstart, "SVG ");
- if (t) {
- stbtt_uint32 offset = ttULONG(info->data + t + 2);
- info->svg = t + offset;
- } else {
- info->svg = 0;
- }
- }
- return info->svg;
-}
-
-static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart)
-{
- stbtt_uint32 cmap, t;
- stbtt_int32 i,numTables;
-
- info->data = data;
- info->fontstart = fontstart;
- info->cff = stbtt__new_buf(NULL, 0);
-
- cmap = stbtt__find_table(data, fontstart, "cmap"); // required
- info->loca = stbtt__find_table(data, fontstart, "loca"); // required
- info->head = stbtt__find_table(data, fontstart, "head"); // required
- info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required
- info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required
- info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required
- info->kern = stbtt__find_table(data, fontstart, "kern"); // not required
- info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required
-
- if (!cmap || !info->head || !info->hhea || !info->hmtx)
- return 0;
- if (info->glyf) {
- // required for truetype
- if (!info->loca) return 0;
- } else {
- // initialization for CFF / Type2 fonts (OTF)
- stbtt__buf b, topdict, topdictidx;
- stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0;
- stbtt_uint32 cff;
-
- cff = stbtt__find_table(data, fontstart, "CFF ");
- if (!cff) return 0;
-
- info->fontdicts = stbtt__new_buf(NULL, 0);
- info->fdselect = stbtt__new_buf(NULL, 0);
-
- // @TODO this should use size from table (not 512MB)
- info->cff = stbtt__new_buf(data+cff, 512*1024*1024);
- b = info->cff;
-
- // read the header
- stbtt__buf_skip(&b, 2);
- stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize
-
- // @TODO the name INDEX could list multiple fonts,
- // but we just use the first one.
- stbtt__cff_get_index(&b); // name INDEX
- topdictidx = stbtt__cff_get_index(&b);
- topdict = stbtt__cff_index_get(topdictidx, 0);
- stbtt__cff_get_index(&b); // string INDEX
- info->gsubrs = stbtt__cff_get_index(&b);
-
- stbtt__dict_get_ints(&topdict, 17, 1, &charstrings);
- stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype);
- stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff);
- stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff);
- info->subrs = stbtt__get_subrs(b, topdict);
-
- // we only support Type 2 charstrings
- if (cstype != 2) return 0;
- if (charstrings == 0) return 0;
-
- if (fdarrayoff) {
- // looks like a CID font
- if (!fdselectoff) return 0;
- stbtt__buf_seek(&b, fdarrayoff);
- info->fontdicts = stbtt__cff_get_index(&b);
- info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff);
- }
-
- stbtt__buf_seek(&b, charstrings);
- info->charstrings = stbtt__cff_get_index(&b);
- }
-
- t = stbtt__find_table(data, fontstart, "maxp");
- if (t)
- info->numGlyphs = ttUSHORT(data+t+4);
- else
- info->numGlyphs = 0xffff;
-
- info->svg = -1;
-
- // find a cmap encoding table we understand *now* to avoid searching
- // later. (todo: could make this installable)
- // the same regardless of glyph.
- numTables = ttUSHORT(data + cmap + 2);
- info->index_map = 0;
- for (i=0; i < numTables; ++i) {
- stbtt_uint32 encoding_record = cmap + 4 + 8 * i;
- // find an encoding we understand:
- switch(ttUSHORT(data+encoding_record)) {
- case STBTT_PLATFORM_ID_MICROSOFT:
- switch (ttUSHORT(data+encoding_record+2)) {
- case STBTT_MS_EID_UNICODE_BMP:
- case STBTT_MS_EID_UNICODE_FULL:
- // MS/Unicode
- info->index_map = cmap + ttULONG(data+encoding_record+4);
- break;
- }
- break;
- case STBTT_PLATFORM_ID_UNICODE:
- // Mac/iOS has these
- // all the encodingIDs are unicode, so we don't bother to check it
- info->index_map = cmap + ttULONG(data+encoding_record+4);
- break;
- }
- }
- if (info->index_map == 0)
- return 0;
-
- info->indexToLocFormat = ttUSHORT(data+info->head + 50);
- return 1;
-}
-
-STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
-{
- stbtt_uint8 *data = info->data;
- stbtt_uint32 index_map = info->index_map;
-
- stbtt_uint16 format = ttUSHORT(data + index_map + 0);
- if (format == 0) { // apple byte encoding
- stbtt_int32 bytes = ttUSHORT(data + index_map + 2);
- if (unicode_codepoint < bytes-6)
- return ttBYTE(data + index_map + 6 + unicode_codepoint);
- return 0;
- } else if (format == 6) {
- stbtt_uint32 first = ttUSHORT(data + index_map + 6);
- stbtt_uint32 count = ttUSHORT(data + index_map + 8);
- if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)
- return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);
- return 0;
- } else if (format == 2) {
- STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean
- return 0;
- } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges
- stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;
- stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;
- stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);
- stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;
-
- // do a binary search of the segments
- stbtt_uint32 endCount = index_map + 14;
- stbtt_uint32 search = endCount;
-
- if (unicode_codepoint > 0xffff)
- return 0;
-
- // they lie from endCount .. endCount + segCount
- // but searchRange is the nearest power of two, so...
- if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))
- search += rangeShift*2;
-
- // now decrement to bias correctly to find smallest
- search -= 2;
- while (entrySelector) {
- stbtt_uint16 end;
- searchRange >>= 1;
- end = ttUSHORT(data + search + searchRange*2);
- if (unicode_codepoint > end)
- search += searchRange*2;
- --entrySelector;
- }
- search += 2;
-
- {
- stbtt_uint16 offset, start, last;
- stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);
-
- start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
- last = ttUSHORT(data + endCount + 2*item);
- if (unicode_codepoint < start || unicode_codepoint > last)
- return 0;
-
- offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
- if (offset == 0)
- return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));
-
- return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
- }
- } else if (format == 12 || format == 13) {
- stbtt_uint32 ngroups = ttULONG(data+index_map+12);
- stbtt_int32 low,high;
- low = 0; high = (stbtt_int32)ngroups;
- // Binary search the right group.
- while (low < high) {
- stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high
- stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);
- stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);
- if ((stbtt_uint32) unicode_codepoint < start_char)
- high = mid;
- else if ((stbtt_uint32) unicode_codepoint > end_char)
- low = mid+1;
- else {
- stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);
- if (format == 12)
- return start_glyph + unicode_codepoint-start_char;
- else // format == 13
- return start_glyph;
- }
- }
- return 0; // not found
- }
- // @TODO
- STBTT_assert(0);
- return 0;
-}
-
-STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)
-{
- return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);
-}
-
-static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy)
-{
- v->type = type;
- v->x = (stbtt_int16) x;
- v->y = (stbtt_int16) y;
- v->cx = (stbtt_int16) cx;
- v->cy = (stbtt_int16) cy;
-}
-
-static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
-{
- int g1,g2;
-
- STBTT_assert(!info->cff.size);
-
- if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range
- if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format
-
- if (info->indexToLocFormat == 0) {
- g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;
- g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;
- } else {
- g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);
- g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);
- }
-
- return g1==g2 ? -1 : g1; // if length is 0, return -1
-}
-
-static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
-
-STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
-{
- if (info->cff.size) {
- stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1);
- } else {
- int g = stbtt__GetGlyfOffset(info, glyph_index);
- if (g < 0) return 0;
-
- if (x0) *x0 = ttSHORT(info->data + g + 2);
- if (y0) *y0 = ttSHORT(info->data + g + 4);
- if (x1) *x1 = ttSHORT(info->data + g + 6);
- if (y1) *y1 = ttSHORT(info->data + g + 8);
- }
- return 1;
-}
-
-STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)
-{
- return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);
-}
-
-STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)
-{
- stbtt_int16 numberOfContours;
- int g;
- if (info->cff.size)
- return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0;
- g = stbtt__GetGlyfOffset(info, glyph_index);
- if (g < 0) return 1;
- numberOfContours = ttSHORT(info->data + g);
- return numberOfContours == 0;
-}
-
-static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off,
- stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy)
-{
- if (start_off) {
- if (was_off)
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy);
- } else {
- if (was_off)
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
- else
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
- }
- return num_vertices;
-}
-
-static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
-{
- stbtt_int16 numberOfContours;
- stbtt_uint8 *endPtsOfContours;
- stbtt_uint8 *data = info->data;
- stbtt_vertex *vertices=0;
- int num_vertices=0;
- int g = stbtt__GetGlyfOffset(info, glyph_index);
-
- *pvertices = NULL;
-
- if (g < 0) return 0;
-
- numberOfContours = ttSHORT(data + g);
-
- if (numberOfContours > 0) {
- stbtt_uint8 flags=0,flagcount;
- stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0;
- stbtt_int32 x,y,cx,cy,sx,sy, scx,scy;
- stbtt_uint8 *points;
- endPtsOfContours = (data + g + 10);
- ins = ttUSHORT(data + g + 10 + numberOfContours * 2);
- points = data + g + 10 + numberOfContours * 2 + 2 + ins;
-
- n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);
-
- m = n + 2*numberOfContours; // a loose bound on how many vertices we might need
- vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);
- if (vertices == 0)
- return 0;
-
- next_move = 0;
- flagcount=0;
-
- // in first pass, we load uninterpreted data into the allocated array
- // above, shifted to the end of the array so we won't overwrite it when
- // we create our final data starting from the front
-
- off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated
-
- // first load flags
-
- for (i=0; i < n; ++i) {
- if (flagcount == 0) {
- flags = *points++;
- if (flags & 8)
- flagcount = *points++;
- } else
- --flagcount;
- vertices[off+i].type = flags;
- }
-
- // now load x coordinates
- x=0;
- for (i=0; i < n; ++i) {
- flags = vertices[off+i].type;
- if (flags & 2) {
- stbtt_int16 dx = *points++;
- x += (flags & 16) ? dx : -dx; // ???
- } else {
- if (!(flags & 16)) {
- x = x + (stbtt_int16) (points[0]*256 + points[1]);
- points += 2;
- }
- }
- vertices[off+i].x = (stbtt_int16) x;
- }
-
- // now load y coordinates
- y=0;
- for (i=0; i < n; ++i) {
- flags = vertices[off+i].type;
- if (flags & 4) {
- stbtt_int16 dy = *points++;
- y += (flags & 32) ? dy : -dy; // ???
- } else {
- if (!(flags & 32)) {
- y = y + (stbtt_int16) (points[0]*256 + points[1]);
- points += 2;
- }
- }
- vertices[off+i].y = (stbtt_int16) y;
- }
-
- // now convert them to our format
- num_vertices=0;
- sx = sy = cx = cy = scx = scy = 0;
- for (i=0; i < n; ++i) {
- flags = vertices[off+i].type;
- x = (stbtt_int16) vertices[off+i].x;
- y = (stbtt_int16) vertices[off+i].y;
-
- if (next_move == i) {
- if (i != 0)
- num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
-
- // now start the new one
- start_off = !(flags & 1);
- if (start_off) {
- // if we start off with an off-curve point, then when we need to find a point on the curve
- // where we can start, and we need to save some state for when we wraparound.
- scx = x;
- scy = y;
- if (!(vertices[off+i+1].type & 1)) {
- // next point is also a curve point, so interpolate an on-point curve
- sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1;
- sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1;
- } else {
- // otherwise just use the next point as our start point
- sx = (stbtt_int32) vertices[off+i+1].x;
- sy = (stbtt_int32) vertices[off+i+1].y;
- ++i; // we're using point i+1 as the starting point, so skip it
- }
- } else {
- sx = x;
- sy = y;
- }
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0);
- was_off = 0;
- next_move = 1 + ttUSHORT(endPtsOfContours+j*2);
- ++j;
- } else {
- if (!(flags & 1)) { // if it's a curve
- if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);
- cx = x;
- cy = y;
- was_off = 1;
- } else {
- if (was_off)
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);
- else
- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);
- was_off = 0;
- }
- }
- }
- num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
- } else if (numberOfContours < 0) {
- // Compound shapes.
- int more = 1;
- stbtt_uint8 *comp = data + g + 10;
- num_vertices = 0;
- vertices = 0;
- while (more) {
- stbtt_uint16 flags, gidx;
- int comp_num_verts = 0, i;
- stbtt_vertex *comp_verts = 0, *tmp = 0;
- float mtx[6] = {1,0,0,1,0,0}, m, n;
-
- flags = ttSHORT(comp); comp+=2;
- gidx = ttSHORT(comp); comp+=2;
-
- if (flags & 2) { // XY values
- if (flags & 1) { // shorts
- mtx[4] = ttSHORT(comp); comp+=2;
- mtx[5] = ttSHORT(comp); comp+=2;
- } else {
- mtx[4] = ttCHAR(comp); comp+=1;
- mtx[5] = ttCHAR(comp); comp+=1;
- }
- }
- else {
- // @TODO handle matching point
- STBTT_assert(0);
- }
- if (flags & (1<<3)) { // WE_HAVE_A_SCALE
- mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
- mtx[1] = mtx[2] = 0;
- } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE
- mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
- mtx[1] = mtx[2] = 0;
- mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
- } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO
- mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
- mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;
- mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;
- mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
- }
-
- // Find transformation scales.
- m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
- n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
-
- // Get indexed glyph.
- comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);
- if (comp_num_verts > 0) {
- // Transform vertices.
- for (i = 0; i < comp_num_verts; ++i) {
- stbtt_vertex* v = &comp_verts[i];
- stbtt_vertex_type x,y;
- x=v->x; y=v->y;
- v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
- v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
- x=v->cx; y=v->cy;
- v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
- v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
- }
- // Append vertices.
- tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);
- if (!tmp) {
- if (vertices) STBTT_free(vertices, info->userdata);
- if (comp_verts) STBTT_free(comp_verts, info->userdata);
- return 0;
- }
- if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
- STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
- if (vertices) STBTT_free(vertices, info->userdata);
- vertices = tmp;
- STBTT_free(comp_verts, info->userdata);
- num_vertices += comp_num_verts;
- }
- // More components ?
- more = flags & (1<<5);
- }
- } else {
- // numberOfCounters == 0, do nothing
- }
-
- *pvertices = vertices;
- return num_vertices;
-}
-
-typedef struct
-{
- int bounds;
- int started;
- float first_x, first_y;
- float x, y;
- stbtt_int32 min_x, max_x, min_y, max_y;
-
- stbtt_vertex *pvertices;
- int num_vertices;
-} stbtt__csctx;
-
-#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0}
-
-static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y)
-{
- if (x > c->max_x || !c->started) c->max_x = x;
- if (y > c->max_y || !c->started) c->max_y = y;
- if (x < c->min_x || !c->started) c->min_x = x;
- if (y < c->min_y || !c->started) c->min_y = y;
- c->started = 1;
-}
-
-static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1)
-{
- if (c->bounds) {
- stbtt__track_vertex(c, x, y);
- if (type == STBTT_vcubic) {
- stbtt__track_vertex(c, cx, cy);
- stbtt__track_vertex(c, cx1, cy1);
- }
- } else {
- stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy);
- c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1;
- c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1;
- }
- c->num_vertices++;
-}
-
-static void stbtt__csctx_close_shape(stbtt__csctx *ctx)
-{
- if (ctx->first_x != ctx->x || ctx->first_y != ctx->y)
- stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0);
-}
-
-static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy)
-{
- stbtt__csctx_close_shape(ctx);
- ctx->first_x = ctx->x = ctx->x + dx;
- ctx->first_y = ctx->y = ctx->y + dy;
- stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
-}
-
-static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy)
-{
- ctx->x += dx;
- ctx->y += dy;
- stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
-}
-
-static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3)
-{
- float cx1 = ctx->x + dx1;
- float cy1 = ctx->y + dy1;
- float cx2 = cx1 + dx2;
- float cy2 = cy1 + dy2;
- ctx->x = cx2 + dx3;
- ctx->y = cy2 + dy3;
- stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2);
-}
-
-static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n)
-{
- int count = stbtt__cff_index_count(&idx);
- int bias = 107;
- if (count >= 33900)
- bias = 32768;
- else if (count >= 1240)
- bias = 1131;
- n += bias;
- if (n < 0 || n >= count)
- return stbtt__new_buf(NULL, 0);
- return stbtt__cff_index_get(idx, n);
-}
-
-static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index)
-{
- stbtt__buf fdselect = info->fdselect;
- int nranges, start, end, v, fmt, fdselector = -1, i;
-
- stbtt__buf_seek(&fdselect, 0);
- fmt = stbtt__buf_get8(&fdselect);
- if (fmt == 0) {
- // untested
- stbtt__buf_skip(&fdselect, glyph_index);
- fdselector = stbtt__buf_get8(&fdselect);
- } else if (fmt == 3) {
- nranges = stbtt__buf_get16(&fdselect);
- start = stbtt__buf_get16(&fdselect);
- for (i = 0; i < nranges; i++) {
- v = stbtt__buf_get8(&fdselect);
- end = stbtt__buf_get16(&fdselect);
- if (glyph_index >= start && glyph_index < end) {
- fdselector = v;
- break;
- }
- start = end;
- }
- }
- if (fdselector == -1) stbtt__new_buf(NULL, 0);
- return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector));
-}
-
-static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c)
-{
- int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0;
- int has_subrs = 0, clear_stack;
- float s[48];
- stbtt__buf subr_stack[10], subrs = info->subrs, b;
- float f;
-
-#define STBTT__CSERR(s) (0)
-
- // this currently ignores the initial width value, which isn't needed if we have hmtx
- b = stbtt__cff_index_get(info->charstrings, glyph_index);
- while (b.cursor < b.size) {
- i = 0;
- clear_stack = 1;
- b0 = stbtt__buf_get8(&b);
- switch (b0) {
- // @TODO implement hinting
- case 0x13: // hintmask
- case 0x14: // cntrmask
- if (in_header)
- maskbits += (sp / 2); // implicit "vstem"
- in_header = 0;
- stbtt__buf_skip(&b, (maskbits + 7) / 8);
- break;
-
- case 0x01: // hstem
- case 0x03: // vstem
- case 0x12: // hstemhm
- case 0x17: // vstemhm
- maskbits += (sp / 2);
- break;
-
- case 0x15: // rmoveto
- in_header = 0;
- if (sp < 2) return STBTT__CSERR("rmoveto stack");
- stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]);
- break;
- case 0x04: // vmoveto
- in_header = 0;
- if (sp < 1) return STBTT__CSERR("vmoveto stack");
- stbtt__csctx_rmove_to(c, 0, s[sp-1]);
- break;
- case 0x16: // hmoveto
- in_header = 0;
- if (sp < 1) return STBTT__CSERR("hmoveto stack");
- stbtt__csctx_rmove_to(c, s[sp-1], 0);
- break;
-
- case 0x05: // rlineto
- if (sp < 2) return STBTT__CSERR("rlineto stack");
- for (; i + 1 < sp; i += 2)
- stbtt__csctx_rline_to(c, s[i], s[i+1]);
- break;
-
- // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical
- // starting from a different place.
-
- case 0x07: // vlineto
- if (sp < 1) return STBTT__CSERR("vlineto stack");
- goto vlineto;
- case 0x06: // hlineto
- if (sp < 1) return STBTT__CSERR("hlineto stack");
- for (;;) {
- if (i >= sp) break;
- stbtt__csctx_rline_to(c, s[i], 0);
- i++;
- vlineto:
- if (i >= sp) break;
- stbtt__csctx_rline_to(c, 0, s[i]);
- i++;
- }
- break;
-
- case 0x1F: // hvcurveto
- if (sp < 4) return STBTT__CSERR("hvcurveto stack");
- goto hvcurveto;
- case 0x1E: // vhcurveto
- if (sp < 4) return STBTT__CSERR("vhcurveto stack");
- for (;;) {
- if (i + 3 >= sp) break;
- stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f);
- i += 4;
- hvcurveto:
- if (i + 3 >= sp) break;
- stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]);
- i += 4;
- }
- break;
-
- case 0x08: // rrcurveto
- if (sp < 6) return STBTT__CSERR("rcurveline stack");
- for (; i + 5 < sp; i += 6)
- stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
- break;
-
- case 0x18: // rcurveline
- if (sp < 8) return STBTT__CSERR("rcurveline stack");
- for (; i + 5 < sp - 2; i += 6)
- stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
- if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack");
- stbtt__csctx_rline_to(c, s[i], s[i+1]);
- break;
-
- case 0x19: // rlinecurve
- if (sp < 8) return STBTT__CSERR("rlinecurve stack");
- for (; i + 1 < sp - 6; i += 2)
- stbtt__csctx_rline_to(c, s[i], s[i+1]);
- if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack");
- stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
- break;
-
- case 0x1A: // vvcurveto
- case 0x1B: // hhcurveto
- if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack");
- f = 0.0;
- if (sp & 1) { f = s[i]; i++; }
- for (; i + 3 < sp; i += 4) {
- if (b0 == 0x1B)
- stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0);
- else
- stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]);
- f = 0.0;
- }
- break;
-
- case 0x0A: // callsubr
- if (!has_subrs) {
- if (info->fdselect.size)
- subrs = stbtt__cid_get_glyph_subrs(info, glyph_index);
- has_subrs = 1;
- }
- // FALLTHROUGH
- case 0x1D: // callgsubr
- if (sp < 1) return STBTT__CSERR("call(g|)subr stack");
- v = (int) s[--sp];
- if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit");
- subr_stack[subr_stack_height++] = b;
- b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v);
- if (b.size == 0) return STBTT__CSERR("subr not found");
- b.cursor = 0;
- clear_stack = 0;
- break;
-
- case 0x0B: // return
- if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr");
- b = subr_stack[--subr_stack_height];
- clear_stack = 0;
- break;
-
- case 0x0E: // endchar
- stbtt__csctx_close_shape(c);
- return 1;
-
- case 0x0C: { // two-byte escape
- float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6;
- float dx, dy;
- int b1 = stbtt__buf_get8(&b);
- switch (b1) {
- // @TODO These "flex" implementations ignore the flex-depth and resolution,
- // and always draw beziers.
- case 0x22: // hflex
- if (sp < 7) return STBTT__CSERR("hflex stack");
- dx1 = s[0];
- dx2 = s[1];
- dy2 = s[2];
- dx3 = s[3];
- dx4 = s[4];
- dx5 = s[5];
- dx6 = s[6];
- stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0);
- stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0);
- break;
-
- case 0x23: // flex
- if (sp < 13) return STBTT__CSERR("flex stack");
- dx1 = s[0];
- dy1 = s[1];
- dx2 = s[2];
- dy2 = s[3];
- dx3 = s[4];
- dy3 = s[5];
- dx4 = s[6];
- dy4 = s[7];
- dx5 = s[8];
- dy5 = s[9];
- dx6 = s[10];
- dy6 = s[11];
- //fd is s[12]
- stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
- stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
- break;
-
- case 0x24: // hflex1
- if (sp < 9) return STBTT__CSERR("hflex1 stack");
- dx1 = s[0];
- dy1 = s[1];
- dx2 = s[2];
- dy2 = s[3];
- dx3 = s[4];
- dx4 = s[5];
- dx5 = s[6];
- dy5 = s[7];
- dx6 = s[8];
- stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0);
- stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5));
- break;
-
- case 0x25: // flex1
- if (sp < 11) return STBTT__CSERR("flex1 stack");
- dx1 = s[0];
- dy1 = s[1];
- dx2 = s[2];
- dy2 = s[3];
- dx3 = s[4];
- dy3 = s[5];
- dx4 = s[6];
- dy4 = s[7];
- dx5 = s[8];
- dy5 = s[9];
- dx6 = dy6 = s[10];
- dx = dx1+dx2+dx3+dx4+dx5;
- dy = dy1+dy2+dy3+dy4+dy5;
- if (STBTT_fabs(dx) > STBTT_fabs(dy))
- dy6 = -dy;
- else
- dx6 = -dx;
- stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
- stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
- break;
-
- default:
- return STBTT__CSERR("unimplemented");
- }
- } break;
-
- default:
- if (b0 != 255 && b0 != 28 && b0 < 32)
- return STBTT__CSERR("reserved operator");
-
- // push immediate
- if (b0 == 255) {
- f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000;
- } else {
- stbtt__buf_skip(&b, -1);
- f = (float)(stbtt_int16)stbtt__cff_int(&b);
- }
- if (sp >= 48) return STBTT__CSERR("push stack overflow");
- s[sp++] = f;
- clear_stack = 0;
- break;
- }
- if (clear_stack) sp = 0;
- }
- return STBTT__CSERR("no endchar");
-
-#undef STBTT__CSERR
-}
-
-static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
-{
- // runs the charstring twice, once to count and once to output (to avoid realloc)
- stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1);
- stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0);
- if (stbtt__run_charstring(info, glyph_index, &count_ctx)) {
- *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata);
- output_ctx.pvertices = *pvertices;
- if (stbtt__run_charstring(info, glyph_index, &output_ctx)) {
- STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices);
- return output_ctx.num_vertices;
- }
- }
- *pvertices = NULL;
- return 0;
-}
-
-static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
-{
- stbtt__csctx c = STBTT__CSCTX_INIT(1);
- int r = stbtt__run_charstring(info, glyph_index, &c);
- if (x0) *x0 = r ? c.min_x : 0;
- if (y0) *y0 = r ? c.min_y : 0;
- if (x1) *x1 = r ? c.max_x : 0;
- if (y1) *y1 = r ? c.max_y : 0;
- return r ? c.num_vertices : 0;
-}
-
-STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
-{
- if (!info->cff.size)
- return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices);
- else
- return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices);
-}
-
-STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
-{
- stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);
- if (glyph_index < numOfLongHorMetrics) {
- if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index);
- if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);
- } else {
- if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));
- if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));
- }
-}
-
-STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info)
-{
- stbtt_uint8 *data = info->data + info->kern;
-
- // we only look at the first table. it must be 'horizontal' and format 0.
- if (!info->kern)
- return 0;
- if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
- return 0;
- if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
- return 0;
-
- return ttUSHORT(data+10);
-}
-
-STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length)
-{
- stbtt_uint8 *data = info->data + info->kern;
- int k, length;
-
- // we only look at the first table. it must be 'horizontal' and format 0.
- if (!info->kern)
- return 0;
- if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
- return 0;
- if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
- return 0;
-
- length = ttUSHORT(data+10);
- if (table_length < length)
- length = table_length;
-
- for (k = 0; k < length; k++)
- {
- table[k].glyph1 = ttUSHORT(data+18+(k*6));
- table[k].glyph2 = ttUSHORT(data+20+(k*6));
- table[k].advance = ttSHORT(data+22+(k*6));
- }
-
- return length;
-}
-
-static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
-{
- stbtt_uint8 *data = info->data + info->kern;
- stbtt_uint32 needle, straw;
- int l, r, m;
-
- // we only look at the first table. it must be 'horizontal' and format 0.
- if (!info->kern)
- return 0;
- if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
- return 0;
- if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
- return 0;
-
- l = 0;
- r = ttUSHORT(data+10) - 1;
- needle = glyph1 << 16 | glyph2;
- while (l <= r) {
- m = (l + r) >> 1;
- straw = ttULONG(data+18+(m*6)); // note: unaligned read
- if (needle < straw)
- r = m - 1;
- else if (needle > straw)
- l = m + 1;
- else
- return ttSHORT(data+22+(m*6));
- }
- return 0;
-}
-
-static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)
-{
- stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
- switch (coverageFormat) {
- case 1: {
- stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
-
- // Binary search.
- stbtt_int32 l=0, r=glyphCount-1, m;
- int straw, needle=glyph;
- while (l <= r) {
- stbtt_uint8 *glyphArray = coverageTable + 4;
- stbtt_uint16 glyphID;
- m = (l + r) >> 1;
- glyphID = ttUSHORT(glyphArray + 2 * m);
- straw = glyphID;
- if (needle < straw)
- r = m - 1;
- else if (needle > straw)
- l = m + 1;
- else {
- return m;
- }
- }
- break;
- }
-
- case 2: {
- stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
- stbtt_uint8 *rangeArray = coverageTable + 4;
-
- // Binary search.
- stbtt_int32 l=0, r=rangeCount-1, m;
- int strawStart, strawEnd, needle=glyph;
- while (l <= r) {
- stbtt_uint8 *rangeRecord;
- m = (l + r) >> 1;
- rangeRecord = rangeArray + 6 * m;
- strawStart = ttUSHORT(rangeRecord);
- strawEnd = ttUSHORT(rangeRecord + 2);
- if (needle < strawStart)
- r = m - 1;
- else if (needle > strawEnd)
- l = m + 1;
- else {
- stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
- return startCoverageIndex + glyph - strawStart;
- }
- }
- break;
- }
-
- default: return -1; // unsupported
- }
-
- return -1;
-}
-
-static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)
-{
- stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
- switch (classDefFormat)
- {
- case 1: {
- stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
- stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
- stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
-
- if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
- return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
- break;
- }
-
- case 2: {
- stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
- stbtt_uint8 *classRangeRecords = classDefTable + 4;
-
- // Binary search.
- stbtt_int32 l=0, r=classRangeCount-1, m;
- int strawStart, strawEnd, needle=glyph;
- while (l <= r) {
- stbtt_uint8 *classRangeRecord;
- m = (l + r) >> 1;
- classRangeRecord = classRangeRecords + 6 * m;
- strawStart = ttUSHORT(classRangeRecord);
- strawEnd = ttUSHORT(classRangeRecord + 2);
- if (needle < strawStart)
- r = m - 1;
- else if (needle > strawEnd)
- l = m + 1;
- else
- return (stbtt_int32)ttUSHORT(classRangeRecord + 4);
- }
- break;
- }
-
- default:
- return -1; // Unsupported definition type, return an error.
- }
-
- // "All glyphs not assigned to a class fall into class 0". (OpenType spec)
- return 0;
-}
-
-// Define to STBTT_assert(x) if you want to break on unimplemented formats.
-#define STBTT_GPOS_TODO_assert(x)
-
-static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
-{
- stbtt_uint16 lookupListOffset;
- stbtt_uint8 *lookupList;
- stbtt_uint16 lookupCount;
- stbtt_uint8 *data;
- stbtt_int32 i, sti;
-
- if (!info->gpos) return 0;
-
- data = info->data + info->gpos;
-
- if (ttUSHORT(data+0) != 1) return 0; // Major version 1
- if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
-
- lookupListOffset = ttUSHORT(data+8);
- lookupList = data + lookupListOffset;
- lookupCount = ttUSHORT(lookupList);
-
- for (i=0; i<lookupCount; ++i) {
- stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
- stbtt_uint8 *lookupTable = lookupList + lookupOffset;
-
- stbtt_uint16 lookupType = ttUSHORT(lookupTable);
- stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
- stbtt_uint8 *subTableOffsets = lookupTable + 6;
- if (lookupType != 2) // Pair Adjustment Positioning Subtable
- continue;
-
- for (sti=0; sti<subTableCount; sti++) {
- stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
- stbtt_uint8 *table = lookupTable + subtableOffset;
- stbtt_uint16 posFormat = ttUSHORT(table);
- stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
- stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
- if (coverageIndex == -1) continue;
-
- switch (posFormat) {
- case 1: {
- stbtt_int32 l, r, m;
- int straw, needle;
- stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
- stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
- if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?
- stbtt_int32 valueRecordPairSizeInBytes = 2;
- stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
- stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
- stbtt_uint8 *pairValueTable = table + pairPosOffset;
- stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
- stbtt_uint8 *pairValueArray = pairValueTable + 2;
-
- if (coverageIndex >= pairSetCount) return 0;
-
- needle=glyph2;
- r=pairValueCount-1;
- l=0;
-
- // Binary search.
- while (l <= r) {
- stbtt_uint16 secondGlyph;
- stbtt_uint8 *pairValue;
- m = (l + r) >> 1;
- pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
- secondGlyph = ttUSHORT(pairValue);
- straw = secondGlyph;
- if (needle < straw)
- r = m - 1;
- else if (needle > straw)
- l = m + 1;
- else {
- stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
- return xAdvance;
- }
- }
- } else
- return 0;
- break;
- }
-
- case 2: {
- stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
- stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
- if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?
- stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);
- stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);
- int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);
- int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);
-
- stbtt_uint16 class1Count = ttUSHORT(table + 12);
- stbtt_uint16 class2Count = ttUSHORT(table + 14);
- stbtt_uint8 *class1Records, *class2Records;
- stbtt_int16 xAdvance;
-
- if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed
- if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed
-
- class1Records = table + 16;
- class2Records = class1Records + 2 * (glyph1class * class2Count);
- xAdvance = ttSHORT(class2Records + 2 * glyph2class);
- return xAdvance;
- } else
- return 0;
- break;
- }
-
- default:
- return 0; // Unsupported position format
- }
- }
- }
-
- return 0;
-}
-
-STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2)
-{
- int xAdvance = 0;
-
- if (info->gpos)
- xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2);
- else if (info->kern)
- xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2);
-
- return xAdvance;
-}
-
-STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)
-{
- if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs
- return 0;
- return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));
-}
-
-STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)
-{
- stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);
-}
-
-STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)
-{
- if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4);
- if (descent) *descent = ttSHORT(info->data+info->hhea + 6);
- if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);
-}
-
-STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap)
-{
- int tab = stbtt__find_table(info->data, info->fontstart, "OS/2");
- if (!tab)
- return 0;
- if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68);
- if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70);
- if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72);
- return 1;
-}
-
-STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)
-{
- *x0 = ttSHORT(info->data + info->head + 36);
- *y0 = ttSHORT(info->data + info->head + 38);
- *x1 = ttSHORT(info->data + info->head + 40);
- *y1 = ttSHORT(info->data + info->head + 42);
-}
-
-STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)
-{
- int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);
- return (float) height / fheight;
-}
-
-STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)
-{
- int unitsPerEm = ttUSHORT(info->data + info->head + 18);
- return pixels / unitsPerEm;
-}
-
-STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
-{
- STBTT_free(v, info->userdata);
-}
-
-STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl)
-{
- int i;
- stbtt_uint8 *data = info->data;
- stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info);
-
- int numEntries = ttUSHORT(svg_doc_list);
- stbtt_uint8 *svg_docs = svg_doc_list + 2;
-
- for(i=0; i<numEntries; i++) {
- stbtt_uint8 *svg_doc = svg_docs + (12 * i);
- if ((gl >= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2)))
- return svg_doc;
- }
- return 0;
-}
-
-STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg)
-{
- stbtt_uint8 *data = info->data;
- stbtt_uint8 *svg_doc;
-
- if (info->svg == 0)
- return 0;
-
- svg_doc = stbtt_FindSVGDoc(info, gl);
- if (svg_doc != NULL) {
- *svg = (char *) data + info->svg + ttULONG(svg_doc + 4);
- return ttULONG(svg_doc + 8);
- } else {
- return 0;
- }
-}
-
-STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg)
-{
- return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// antialiasing software rasterizer
-//
-
-STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
-{
- int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning
- if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {
- // e.g. space character
- if (ix0) *ix0 = 0;
- if (iy0) *iy0 = 0;
- if (ix1) *ix1 = 0;
- if (iy1) *iy1 = 0;
- } else {
- // move to integral bboxes (treating pixels as little squares, what pixels get touched)?
- if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x);
- if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y);
- if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x);
- if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y);
- }
-}
-
-STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
-{
- stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);
-}
-
-STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
-{
- stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1);
-}
-
-STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
-{
- stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// Rasterizer
-
-typedef struct stbtt__hheap_chunk
-{
- struct stbtt__hheap_chunk *next;
-} stbtt__hheap_chunk;
-
-typedef struct stbtt__hheap
-{
- struct stbtt__hheap_chunk *head;
- void *first_free;
- int num_remaining_in_head_chunk;
-} stbtt__hheap;
-
-static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)
-{
- if (hh->first_free) {
- void *p = hh->first_free;
- hh->first_free = * (void **) p;
- return p;
- } else {
- if (hh->num_remaining_in_head_chunk == 0) {
- int count = (size < 32 ? 2000 : size < 128 ? 800 : 100);
- stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata);
- if (c == NULL)
- return NULL;
- c->next = hh->head;
- hh->head = c;
- hh->num_remaining_in_head_chunk = count;
- }
- --hh->num_remaining_in_head_chunk;
- return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk;
- }
-}
-
-static void stbtt__hheap_free(stbtt__hheap *hh, void *p)
-{
- *(void **) p = hh->first_free;
- hh->first_free = p;
-}
-
-static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata)
-{
- stbtt__hheap_chunk *c = hh->head;
- while (c) {
- stbtt__hheap_chunk *n = c->next;
- STBTT_free(c, userdata);
- c = n;
- }
-}
-
-typedef struct stbtt__edge {
- float x0,y0, x1,y1;
- int invert;
-} stbtt__edge;
-
-
-typedef struct stbtt__active_edge
-{
- struct stbtt__active_edge *next;
- #if STBTT_RASTERIZER_VERSION==1
- int x,dx;
- float ey;
- int direction;
- #elif STBTT_RASTERIZER_VERSION==2
- float fx,fdx,fdy;
- float direction;
- float sy;
- float ey;
- #else
- #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
- #endif
-} stbtt__active_edge;
-
-#if STBTT_RASTERIZER_VERSION == 1
-#define STBTT_FIXSHIFT 10
-#define STBTT_FIX (1 << STBTT_FIXSHIFT)
-#define STBTT_FIXMASK (STBTT_FIX-1)
-
-static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
-{
- stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
- float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
- STBTT_assert(z != NULL);
- if (!z) return z;
-
- // round dx down to avoid overshooting
- if (dxdy < 0)
- z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy);
- else
- z->dx = STBTT_ifloor(STBTT_FIX * dxdy);
-
- z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount
- z->x -= off_x * STBTT_FIX;
-
- z->ey = e->y1;
- z->next = 0;
- z->direction = e->invert ? 1 : -1;
- return z;
-}
-#elif STBTT_RASTERIZER_VERSION == 2
-static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
-{
- stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
- float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
- STBTT_assert(z != NULL);
- //STBTT_assert(e->y0 <= start_point);
- if (!z) return z;
- z->fdx = dxdy;
- z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f;
- z->fx = e->x0 + dxdy * (start_point - e->y0);
- z->fx -= off_x;
- z->direction = e->invert ? 1.0f : -1.0f;
- z->sy = e->y0;
- z->ey = e->y1;
- z->next = 0;
- return z;
-}
-#else
-#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
-#endif
-
-#if STBTT_RASTERIZER_VERSION == 1
-// note: this routine clips fills that extend off the edges... ideally this
-// wouldn't happen, but it could happen if the truetype glyph bounding boxes
-// are wrong, or if the user supplies a too-small bitmap
-static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)
-{
- // non-zero winding fill
- int x0=0, w=0;
-
- while (e) {
- if (w == 0) {
- // if we're currently at zero, we need to record the edge start point
- x0 = e->x; w += e->direction;
- } else {
- int x1 = e->x; w += e->direction;
- // if we went to zero, we need to draw
- if (w == 0) {
- int i = x0 >> STBTT_FIXSHIFT;
- int j = x1 >> STBTT_FIXSHIFT;
-
- if (i < len && j >= 0) {
- if (i == j) {
- // x0,x1 are the same pixel, so compute combined coverage
- scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT);
- } else {
- if (i >= 0) // add antialiasing for x0
- scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT);
- else
- i = -1; // clip
-
- if (j < len) // add antialiasing for x1
- scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT);
- else
- j = len; // clip
-
- for (++i; i < j; ++i) // fill pixels between x0 and x1
- scanline[i] = scanline[i] + (stbtt_uint8) max_weight;
- }
- }
- }
- }
-
- e = e->next;
- }
-}
-
-static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
-{
- stbtt__hheap hh = { 0, 0, 0 };
- stbtt__active_edge *active = NULL;
- int y,j=0;
- int max_weight = (255 / vsubsample); // weight per vertical scanline
- int s; // vertical subsample index
- unsigned char scanline_data[512], *scanline;
-
- if (result->w > 512)
- scanline = (unsigned char *) STBTT_malloc(result->w, userdata);
- else
- scanline = scanline_data;
-
- y = off_y * vsubsample;
- e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;
-
- while (j < result->h) {
- STBTT_memset(scanline, 0, result->w);
- for (s=0; s < vsubsample; ++s) {
- // find center of pixel for this scanline
- float scan_y = y + 0.5f;
- stbtt__active_edge **step = &active;
-
- // update all active edges;
- // remove all active edges that terminate before the center of this scanline
- while (*step) {
- stbtt__active_edge * z = *step;
- if (z->ey <= scan_y) {
- *step = z->next; // delete from list
- STBTT_assert(z->direction);
- z->direction = 0;
- stbtt__hheap_free(&hh, z);
- } else {
- z->x += z->dx; // advance to position for current scanline
- step = &((*step)->next); // advance through list
- }
- }
-
- // resort the list if needed
- for(;;) {
- int changed=0;
- step = &active;
- while (*step && (*step)->next) {
- if ((*step)->x > (*step)->next->x) {
- stbtt__active_edge *t = *step;
- stbtt__active_edge *q = t->next;
-
- t->next = q->next;
- q->next = t;
- *step = q;
- changed = 1;
- }
- step = &(*step)->next;
- }
- if (!changed) break;
- }
-
- // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
- while (e->y0 <= scan_y) {
- if (e->y1 > scan_y) {
- stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata);
- if (z != NULL) {
- // find insertion point
- if (active == NULL)
- active = z;
- else if (z->x < active->x) {
- // insert at front
- z->next = active;
- active = z;
- } else {
- // find thing to insert AFTER
- stbtt__active_edge *p = active;
- while (p->next && p->next->x < z->x)
- p = p->next;
- // at this point, p->next->x is NOT < z->x
- z->next = p->next;
- p->next = z;
- }
- }
- }
- ++e;
- }
-
- // now process all active edges in XOR fashion
- if (active)
- stbtt__fill_active_edges(scanline, result->w, active, max_weight);
-
- ++y;
- }
- STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);
- ++j;
- }
-
- stbtt__hheap_cleanup(&hh, userdata);
-
- if (scanline != scanline_data)
- STBTT_free(scanline, userdata);
-}
-
-#elif STBTT_RASTERIZER_VERSION == 2
-
-// the edge passed in here does not cross the vertical line at x or the vertical line at x+1
-// (i.e. it has already been clipped to those)
-static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1)
-{
- if (y0 == y1) return;
- STBTT_assert(y0 < y1);
- STBTT_assert(e->sy <= e->ey);
- if (y0 > e->ey) return;
- if (y1 < e->sy) return;
- if (y0 < e->sy) {
- x0 += (x1-x0) * (e->sy - y0) / (y1-y0);
- y0 = e->sy;
- }
- if (y1 > e->ey) {
- x1 += (x1-x0) * (e->ey - y1) / (y1-y0);
- y1 = e->ey;
- }
-
- if (x0 == x)
- STBTT_assert(x1 <= x+1);
- else if (x0 == x+1)
- STBTT_assert(x1 >= x);
- else if (x0 <= x)
- STBTT_assert(x1 <= x);
- else if (x0 >= x+1)
- STBTT_assert(x1 >= x+1);
- else
- STBTT_assert(x1 >= x && x1 <= x+1);
-
- if (x0 <= x && x1 <= x)
- scanline[x] += e->direction * (y1-y0);
- else if (x0 >= x+1 && x1 >= x+1)
- ;
- else {
- STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1);
- scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position
- }
-}
-
-static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width)
-{
- STBTT_assert(top_width >= 0);
- STBTT_assert(bottom_width >= 0);
- return (top_width + bottom_width) / 2.0f * height;
-}
-
-static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1)
-{
- return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0);
-}
-
-static float stbtt__sized_triangle_area(float height, float width)
-{
- return height * width / 2;
-}
-
-static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
-{
- float y_bottom = y_top+1;
-
- while (e) {
- // brute force every pixel
-
- // compute intersection points with top & bottom
- STBTT_assert(e->ey >= y_top);
-
- if (e->fdx == 0) {
- float x0 = e->fx;
- if (x0 < len) {
- if (x0 >= 0) {
- stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom);
- stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom);
- } else {
- stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom);
- }
- }
- } else {
- float x0 = e->fx;
- float dx = e->fdx;
- float xb = x0 + dx;
- float x_top, x_bottom;
- float sy0,sy1;
- float dy = e->fdy;
- STBTT_assert(e->sy <= y_bottom && e->ey >= y_top);
-
- // compute endpoints of line segment clipped to this scanline (if the
- // line segment starts on this scanline. x0 is the intersection of the
- // line with y_top, but that may be off the line segment.
- if (e->sy > y_top) {
- x_top = x0 + dx * (e->sy - y_top);
- sy0 = e->sy;
- } else {
- x_top = x0;
- sy0 = y_top;
- }
- if (e->ey < y_bottom) {
- x_bottom = x0 + dx * (e->ey - y_top);
- sy1 = e->ey;
- } else {
- x_bottom = xb;
- sy1 = y_bottom;
- }
-
- if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) {
- // from here on, we don't have to range check x values
-
- if ((int) x_top == (int) x_bottom) {
- float height;
- // simple case, only spans one pixel
- int x = (int) x_top;
- height = (sy1 - sy0) * e->direction;
- STBTT_assert(x >= 0 && x < len);
- scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);
- scanline_fill[x] += height; // everything right of this pixel is filled
- } else {
- int x,x1,x2;
- float y_crossing, y_final, step, sign, area;
- // covers 2+ pixels
- if (x_top > x_bottom) {
- // flip scanline vertically; signed area is the same
- float t;
- sy0 = y_bottom - (sy0 - y_top);
- sy1 = y_bottom - (sy1 - y_top);
- t = sy0, sy0 = sy1, sy1 = t;
- t = x_bottom, x_bottom = x_top, x_top = t;
- dx = -dx;
- dy = -dy;
- t = x0, x0 = xb, xb = t;
- }
- STBTT_assert(dy >= 0);
- STBTT_assert(dx >= 0);
-
- x1 = (int) x_top;
- x2 = (int) x_bottom;
- // compute intersection with y axis at x1+1
- y_crossing = y_top + dy * (x1+1 - x0);
-
- // compute intersection with y axis at x2
- y_final = y_top + dy * (x2 - x0);
-
- // x1 x_top x2 x_bottom
- // y_top +------|-----+------------+------------+--------|---+------------+
- // | | | | | |
- // | | | | | |
- // sy0 | Txxxxx|............|............|............|............|
- // y_crossing | *xxxxx.......|............|............|............|
- // | | xxxxx..|............|............|............|
- // | | /- xx*xxxx........|............|............|
- // | | dy < | xxxxxx..|............|............|
- // y_final | | \- | xx*xxx.........|............|
- // sy1 | | | | xxxxxB...|............|
- // | | | | | |
- // | | | | | |
- // y_bottom +------------+------------+------------+------------+------------+
- //
- // goal is to measure the area covered by '.' in each pixel
-
- // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
- // @TODO: maybe test against sy1 rather than y_bottom?
- if (y_crossing > y_bottom)
- y_crossing = y_bottom;
-
- sign = e->direction;
-
- // area of the rectangle covered from sy0..y_crossing
- area = sign * (y_crossing-sy0);
-
- // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)
- scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top);
-
- // check if final y_crossing is blown up; no test case for this
- if (y_final > y_bottom) {
- y_final = y_bottom;
- dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom
- }
-
- // in second pixel, area covered by line segment found in first pixel
- // is always a rectangle 1 wide * the height of that line segment; this
- // is exactly what the variable 'area' stores. it also gets a contribution
- // from the line segment within it. the THIRD pixel will get the first
- // pixel's rectangle contribution, the second pixel's rectangle contribution,
- // and its own contribution. the 'own contribution' is the same in every pixel except
- // the leftmost and rightmost, a trapezoid that slides down in each pixel.
- // the second pixel's contribution to the third pixel will be the
- // rectangle 1 wide times the height change in the second pixel, which is dy.
-
- step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x,
- // which multiplied by 1-pixel-width is how much pixel area changes for each step in x
- // so the area advances by 'step' every time
-
- for (x = x1+1; x < x2; ++x) {
- scanline[x] += area + step/2; // area of trapezoid is 1*step/2
- area += step;
- }
- STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down
- STBTT_assert(sy1 > y_final-0.01f);
-
- // area covered in the last pixel is the rectangle from all the pixels to the left,
- // plus the trapezoid filled by the line segment in this pixel all the way to the right edge
- scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f);
-
- // the rest of the line is filled based on the total height of the line segment in this pixel
- scanline_fill[x2] += sign * (sy1-sy0);
- }
- } else {
- // if edge goes outside of box we're drawing, we require
- // clipping logic. since this does not match the intended use
- // of this library, we use a different, very slow brute
- // force implementation
- // note though that this does happen some of the time because
- // x_top and x_bottom can be extrapolated at the top & bottom of
- // the shape and actually lie outside the bounding box
- int x;
- for (x=0; x < len; ++x) {
- // cases:
- //
- // there can be up to two intersections with the pixel. any intersection
- // with left or right edges can be handled by splitting into two (or three)
- // regions. intersections with top & bottom do not necessitate case-wise logic.
- //
- // the old way of doing this found the intersections with the left & right edges,
- // then used some simple logic to produce up to three segments in sorted order
- // from top-to-bottom. however, this had a problem: if an x edge was epsilon
- // across the x border, then the corresponding y position might not be distinct
- // from the other y segment, and it might ignored as an empty segment. to avoid
- // that, we need to explicitly produce segments based on x positions.
-
- // rename variables to clearly-defined pairs
- float y0 = y_top;
- float x1 = (float) (x);
- float x2 = (float) (x+1);
- float x3 = xb;
- float y3 = y_bottom;
-
- // x = e->x + e->dx * (y-y_top)
- // (y-y_top) = (x - e->x) / e->dx
- // y = (x - e->x) / e->dx + y_top
- float y1 = (x - x0) / dx + y_top;
- float y2 = (x+1 - x0) / dx + y_top;
-
- if (x0 < x1 && x3 > x2) { // three segments descending down-right
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
- stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2);
- stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
- } else if (x3 < x1 && x0 > x2) { // three segments descending down-left
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
- stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1);
- stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
- } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
- stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
- } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
- stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
- } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
- stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
- } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
- stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
- } else { // one segment
- stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3);
- }
- }
- }
- }
- e = e->next;
- }
-}
-
-// directly AA rasterize edges w/o supersampling
-static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
-{
- stbtt__hheap hh = { 0, 0, 0 };
- stbtt__active_edge *active = NULL;
- int y,j=0, i;
- float scanline_data[129], *scanline, *scanline2;
-
- STBTT__NOTUSED(vsubsample);
-
- if (result->w > 64)
- scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata);
- else
- scanline = scanline_data;
-
- scanline2 = scanline + result->w;
-
- y = off_y;
- e[n].y0 = (float) (off_y + result->h) + 1;
-
- while (j < result->h) {
- // find center of pixel for this scanline
- float scan_y_top = y + 0.0f;
- float scan_y_bottom = y + 1.0f;
- stbtt__active_edge **step = &active;
-
- STBTT_memset(scanline , 0, result->w*sizeof(scanline[0]));
- STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0]));
-
- // update all active edges;
- // remove all active edges that terminate before the top of this scanline
- while (*step) {
- stbtt__active_edge * z = *step;
- if (z->ey <= scan_y_top) {
- *step = z->next; // delete from list
- STBTT_assert(z->direction);
- z->direction = 0;
- stbtt__hheap_free(&hh, z);
- } else {
- step = &((*step)->next); // advance through list
- }
- }
-
- // insert all edges that start before the bottom of this scanline
- while (e->y0 <= scan_y_bottom) {
- if (e->y0 != e->y1) {
- stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata);
- if (z != NULL) {
- if (j == 0 && off_y != 0) {
- if (z->ey < scan_y_top) {
- // this can happen due to subpixel positioning and some kind of fp rounding error i think
- z->ey = scan_y_top;
- }
- }
- STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds
- // insert at front
- z->next = active;
- active = z;
- }
- }
- ++e;
- }
-
- // now process all active edges
- if (active)
- stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top);
-
- {
- float sum = 0;
- for (i=0; i < result->w; ++i) {
- float k;
- int m;
- sum += scanline2[i];
- k = scanline[i] + sum;
- k = (float) STBTT_fabs(k)*255 + 0.5f;
- m = (int) k;
- if (m > 255) m = 255;
- result->pixels[j*result->stride + i] = (unsigned char) m;
- }
- }
- // advance all the edges
- step = &active;
- while (*step) {
- stbtt__active_edge *z = *step;
- z->fx += z->fdx; // advance to position for current scanline
- step = &((*step)->next); // advance through list
- }
-
- ++y;
- ++j;
- }
-
- stbtt__hheap_cleanup(&hh, userdata);
-
- if (scanline != scanline_data)
- STBTT_free(scanline, userdata);
-}
-#else
-#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
-#endif
-
-#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0)
-
-static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n)
-{
- int i,j;
- for (i=1; i < n; ++i) {
- stbtt__edge t = p[i], *a = &t;
- j = i;
- while (j > 0) {
- stbtt__edge *b = &p[j-1];
- int c = STBTT__COMPARE(a,b);
- if (!c) break;
- p[j] = p[j-1];
- --j;
- }
- if (i != j)
- p[j] = t;
- }
-}
-
-static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n)
-{
- /* threshold for transitioning to insertion sort */
- while (n > 12) {
- stbtt__edge t;
- int c01,c12,c,m,i,j;
-
- /* compute median of three */
- m = n >> 1;
- c01 = STBTT__COMPARE(&p[0],&p[m]);
- c12 = STBTT__COMPARE(&p[m],&p[n-1]);
- /* if 0 >= mid >= end, or 0 < mid < end, then use mid */
- if (c01 != c12) {
- /* otherwise, we'll need to swap something else to middle */
- int z;
- c = STBTT__COMPARE(&p[0],&p[n-1]);
- /* 0>mid && mid<n: 0>n => n; 0<n => 0 */
- /* 0<mid && mid>n: 0>n => 0; 0<n => n */
- z = (c == c12) ? 0 : n-1;
- t = p[z];
- p[z] = p[m];
- p[m] = t;
- }
- /* now p[m] is the median-of-three */
- /* swap it to the beginning so it won't move around */
- t = p[0];
- p[0] = p[m];
- p[m] = t;
-
- /* partition loop */
- i=1;
- j=n-1;
- for(;;) {
- /* handling of equality is crucial here */
- /* for sentinels & efficiency with duplicates */
- for (;;++i) {
- if (!STBTT__COMPARE(&p[i], &p[0])) break;
- }
- for (;;--j) {
- if (!STBTT__COMPARE(&p[0], &p[j])) break;
- }
- /* make sure we haven't crossed */
- if (i >= j) break;
- t = p[i];
- p[i] = p[j];
- p[j] = t;
-
- ++i;
- --j;
- }
- /* recurse on smaller side, iterate on larger */
- if (j < (n-i)) {
- stbtt__sort_edges_quicksort(p,j);
- p = p+i;
- n = n-i;
- } else {
- stbtt__sort_edges_quicksort(p+i, n-i);
- n = j;
- }
- }
-}
-
-static void stbtt__sort_edges(stbtt__edge *p, int n)
-{
- stbtt__sort_edges_quicksort(p, n);
- stbtt__sort_edges_ins_sort(p, n);
-}
-
-typedef struct
-{
- float x,y;
-} stbtt__point;
-
-static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata)
-{
- float y_scale_inv = invert ? -scale_y : scale_y;
- stbtt__edge *e;
- int n,i,j,k,m;
-#if STBTT_RASTERIZER_VERSION == 1
- int vsubsample = result->h < 8 ? 15 : 5;
-#elif STBTT_RASTERIZER_VERSION == 2
- int vsubsample = 1;
-#else
- #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
-#endif
- // vsubsample should divide 255 evenly; otherwise we won't reach full opacity
-
- // now we have to blow out the windings into explicit edge lists
- n = 0;
- for (i=0; i < windings; ++i)
- n += wcount[i];
-
- e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel
- if (e == 0) return;
- n = 0;
-
- m=0;
- for (i=0; i < windings; ++i) {
- stbtt__point *p = pts + m;
- m += wcount[i];
- j = wcount[i]-1;
- for (k=0; k < wcount[i]; j=k++) {
- int a=k,b=j;
- // skip the edge if horizontal
- if (p[j].y == p[k].y)
- continue;
- // add edge from j to k to the list
- e[n].invert = 0;
- if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {
- e[n].invert = 1;
- a=j,b=k;
- }
- e[n].x0 = p[a].x * scale_x + shift_x;
- e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample;
- e[n].x1 = p[b].x * scale_x + shift_x;
- e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample;
- ++n;
- }
- }
-
- // now sort the edges by their highest point (should snap to integer, and then by x)
- //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);
- stbtt__sort_edges(e, n);
-
- // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule
- stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);
-
- STBTT_free(e, userdata);
-}
-
-static void stbtt__add_point(stbtt__point *points, int n, float x, float y)
-{
- if (!points) return; // during first pass, it's unallocated
- points[n].x = x;
- points[n].y = y;
-}
-
-// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching
-static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
-{
- // midpoint
- float mx = (x0 + 2*x1 + x2)/4;
- float my = (y0 + 2*y1 + y2)/4;
- // versus directly drawn line
- float dx = (x0+x2)/2 - mx;
- float dy = (y0+y2)/2 - my;
- if (n > 16) // 65536 segments on one curve better be enough!
- return 1;
- if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA
- stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
- stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
- } else {
- stbtt__add_point(points, *num_points,x2,y2);
- *num_points = *num_points+1;
- }
- return 1;
-}
-
-static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n)
-{
- // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough
- float dx0 = x1-x0;
- float dy0 = y1-y0;
- float dx1 = x2-x1;
- float dy1 = y2-y1;
- float dx2 = x3-x2;
- float dy2 = y3-y2;
- float dx = x3-x0;
- float dy = y3-y0;
- float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2));
- float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy);
- float flatness_squared = longlen*longlen-shortlen*shortlen;
-
- if (n > 16) // 65536 segments on one curve better be enough!
- return;
-
- if (flatness_squared > objspace_flatness_squared) {
- float x01 = (x0+x1)/2;
- float y01 = (y0+y1)/2;
- float x12 = (x1+x2)/2;
- float y12 = (y1+y2)/2;
- float x23 = (x2+x3)/2;
- float y23 = (y2+y3)/2;
-
- float xa = (x01+x12)/2;
- float ya = (y01+y12)/2;
- float xb = (x12+x23)/2;
- float yb = (y12+y23)/2;
-
- float mx = (xa+xb)/2;
- float my = (ya+yb)/2;
-
- stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1);
- stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1);
- } else {
- stbtt__add_point(points, *num_points,x3,y3);
- *num_points = *num_points+1;
- }
-}
-
-// returns number of contours
-static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
-{
- stbtt__point *points=0;
- int num_points=0;
-
- float objspace_flatness_squared = objspace_flatness * objspace_flatness;
- int i,n=0,start=0, pass;
-
- // count how many "moves" there are to get the contour count
- for (i=0; i < num_verts; ++i)
- if (vertices[i].type == STBTT_vmove)
- ++n;
-
- *num_contours = n;
- if (n == 0) return 0;
-
- *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);
-
- if (*contour_lengths == 0) {
- *num_contours = 0;
- return 0;
- }
-
- // make two passes through the points so we don't need to realloc
- for (pass=0; pass < 2; ++pass) {
- float x=0,y=0;
- if (pass == 1) {
- points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);
- if (points == NULL) goto error;
- }
- num_points = 0;
- n= -1;
- for (i=0; i < num_verts; ++i) {
- switch (vertices[i].type) {
- case STBTT_vmove:
- // start the next contour
- if (n >= 0)
- (*contour_lengths)[n] = num_points - start;
- ++n;
- start = num_points;
-
- x = vertices[i].x, y = vertices[i].y;
- stbtt__add_point(points, num_points++, x,y);
- break;
- case STBTT_vline:
- x = vertices[i].x, y = vertices[i].y;
- stbtt__add_point(points, num_points++, x, y);
- break;
- case STBTT_vcurve:
- stbtt__tesselate_curve(points, &num_points, x,y,
- vertices[i].cx, vertices[i].cy,
- vertices[i].x, vertices[i].y,
- objspace_flatness_squared, 0);
- x = vertices[i].x, y = vertices[i].y;
- break;
- case STBTT_vcubic:
- stbtt__tesselate_cubic(points, &num_points, x,y,
- vertices[i].cx, vertices[i].cy,
- vertices[i].cx1, vertices[i].cy1,
- vertices[i].x, vertices[i].y,
- objspace_flatness_squared, 0);
- x = vertices[i].x, y = vertices[i].y;
- break;
- }
- }
- (*contour_lengths)[n] = num_points - start;
- }
-
- return points;
-error:
- STBTT_free(points, userdata);
- STBTT_free(*contour_lengths, userdata);
- *contour_lengths = 0;
- *num_contours = 0;
- return NULL;
-}
-
-STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)
-{
- float scale = scale_x > scale_y ? scale_y : scale_x;
- int winding_count = 0;
- int *winding_lengths = NULL;
- stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);
- if (windings) {
- stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata);
- STBTT_free(winding_lengths, userdata);
- STBTT_free(windings, userdata);
- }
-}
-
-STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
-{
- STBTT_free(bitmap, userdata);
-}
-
-STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)
-{
- int ix0,iy0,ix1,iy1;
- stbtt__bitmap gbm;
- stbtt_vertex *vertices;
- int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
-
- if (scale_x == 0) scale_x = scale_y;
- if (scale_y == 0) {
- if (scale_x == 0) {
- STBTT_free(vertices, info->userdata);
- return NULL;
- }
- scale_y = scale_x;
- }
-
- stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1);
-
- // now we get the size
- gbm.w = (ix1 - ix0);
- gbm.h = (iy1 - iy0);
- gbm.pixels = NULL; // in case we error
-
- if (width ) *width = gbm.w;
- if (height) *height = gbm.h;
- if (xoff ) *xoff = ix0;
- if (yoff ) *yoff = iy0;
-
- if (gbm.w && gbm.h) {
- gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);
- if (gbm.pixels) {
- gbm.stride = gbm.w;
-
- stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata);
- }
- }
- STBTT_free(vertices, info->userdata);
- return gbm.pixels;
-}
-
-STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
-{
- return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff);
-}
-
-STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)
-{
- int ix0,iy0;
- stbtt_vertex *vertices;
- int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
- stbtt__bitmap gbm;
-
- stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);
- gbm.pixels = output;
- gbm.w = out_w;
- gbm.h = out_h;
- gbm.stride = out_stride;
-
- if (gbm.w && gbm.h)
- stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata);
-
- STBTT_free(vertices, info->userdata);
-}
-
-STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)
-{
- stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph);
-}
-
-STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
-{
- return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
-}
-
-STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint)
-{
- stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint));
-}
-
-STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)
-{
- stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint));
-}
-
-STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
-{
- return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);
-}
-
-STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
-{
- stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// bitmap baking
-//
-// This is SUPER-CRAPPY packing to keep source code small
-
-static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
- float pixel_height, // height of font in pixels
- unsigned char *pixels, int pw, int ph, // bitmap to be filled in
- int first_char, int num_chars, // characters to bake
- stbtt_bakedchar *chardata)
-{
- float scale;
- int x,y,bottom_y, i;
- stbtt_fontinfo f;
- f.userdata = NULL;
- if (!stbtt_InitFont(&f, data, offset))
- return -1;
- STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
- x=y=1;
- bottom_y = 1;
-
- scale = stbtt_ScaleForPixelHeight(&f, pixel_height);
-
- for (i=0; i < num_chars; ++i) {
- int advance, lsb, x0,y0,x1,y1,gw,gh;
- int g = stbtt_FindGlyphIndex(&f, first_char + i);
- stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);
- stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);
- gw = x1-x0;
- gh = y1-y0;
- if (x + gw + 1 >= pw)
- y = bottom_y, x = 1; // advance to next row
- if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row
- return -i;
- STBTT_assert(x+gw < pw);
- STBTT_assert(y+gh < ph);
- stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);
- chardata[i].x0 = (stbtt_int16) x;
- chardata[i].y0 = (stbtt_int16) y;
- chardata[i].x1 = (stbtt_int16) (x + gw);
- chardata[i].y1 = (stbtt_int16) (y + gh);
- chardata[i].xadvance = scale * advance;
- chardata[i].xoff = (float) x0;
- chardata[i].yoff = (float) y0;
- x = x + gw + 1;
- if (y+gh+1 > bottom_y)
- bottom_y = y+gh+1;
- }
- return bottom_y;
-}
-
-STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
-{
- float d3d_bias = opengl_fillrule ? 0 : -0.5f;
- float ipw = 1.0f / pw, iph = 1.0f / ph;
- const stbtt_bakedchar *b = chardata + char_index;
- int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f);
- int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f);
-
- q->x0 = round_x + d3d_bias;
- q->y0 = round_y + d3d_bias;
- q->x1 = round_x + b->x1 - b->x0 + d3d_bias;
- q->y1 = round_y + b->y1 - b->y0 + d3d_bias;
-
- q->s0 = b->x0 * ipw;
- q->t0 = b->y0 * iph;
- q->s1 = b->x1 * ipw;
- q->t1 = b->y1 * iph;
-
- *xpos += b->xadvance;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// rectangle packing replacement routines if you don't have stb_rect_pack.h
-//
-
-#ifndef STB_RECT_PACK_VERSION
-
-typedef int stbrp_coord;
-
-////////////////////////////////////////////////////////////////////////////////////
-// //
-// //
-// COMPILER WARNING ?!?!? //
-// //
-// //
-// if you get a compile warning due to these symbols being defined more than //
-// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" //
-// //
-////////////////////////////////////////////////////////////////////////////////////
-
-typedef struct
-{
- int width,height;
- int x,y,bottom_y;
-} stbrp_context;
-
-typedef struct
-{
- unsigned char x;
-} stbrp_node;
-
-struct stbrp_rect
-{
- stbrp_coord x,y;
- int id,w,h,was_packed;
-};
-
-static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes)
-{
- con->width = pw;
- con->height = ph;
- con->x = 0;
- con->y = 0;
- con->bottom_y = 0;
- STBTT__NOTUSED(nodes);
- STBTT__NOTUSED(num_nodes);
-}
-
-static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)
-{
- int i;
- for (i=0; i < num_rects; ++i) {
- if (con->x + rects[i].w > con->width) {
- con->x = 0;
- con->y = con->bottom_y;
- }
- if (con->y + rects[i].h > con->height)
- break;
- rects[i].x = con->x;
- rects[i].y = con->y;
- rects[i].was_packed = 1;
- con->x += rects[i].w;
- if (con->y + rects[i].h > con->bottom_y)
- con->bottom_y = con->y + rects[i].h;
- }
- for ( ; i < num_rects; ++i)
- rects[i].was_packed = 0;
-}
-#endif
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// bitmap baking
-//
-// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If
-// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy.
-
-STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)
-{
- stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context);
- int num_nodes = pw - padding;
- stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context);
-
- if (context == NULL || nodes == NULL) {
- if (context != NULL) STBTT_free(context, alloc_context);
- if (nodes != NULL) STBTT_free(nodes , alloc_context);
- return 0;
- }
-
- spc->user_allocator_context = alloc_context;
- spc->width = pw;
- spc->height = ph;
- spc->pixels = pixels;
- spc->pack_info = context;
- spc->nodes = nodes;
- spc->padding = padding;
- spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;
- spc->h_oversample = 1;
- spc->v_oversample = 1;
- spc->skip_missing = 0;
-
- stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);
-
- if (pixels)
- STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
-
- return 1;
-}
-
-STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc)
-{
- STBTT_free(spc->nodes , spc->user_allocator_context);
- STBTT_free(spc->pack_info, spc->user_allocator_context);
-}
-
-STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)
-{
- STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE);
- STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE);
- if (h_oversample <= STBTT_MAX_OVERSAMPLE)
- spc->h_oversample = h_oversample;
- if (v_oversample <= STBTT_MAX_OVERSAMPLE)
- spc->v_oversample = v_oversample;
-}
-
-STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip)
-{
- spc->skip_missing = skip;
-}
-
-#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1)
-
-static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
-{
- unsigned char buffer[STBTT_MAX_OVERSAMPLE];
- int safe_w = w - kernel_width;
- int j;
- STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
- for (j=0; j < h; ++j) {
- int i;
- unsigned int total;
- STBTT_memset(buffer, 0, kernel_width);
-
- total = 0;
-
- // make kernel_width a constant in common cases so compiler can optimize out the divide
- switch (kernel_width) {
- case 2:
- for (i=0; i <= safe_w; ++i) {
- total += pixels[i] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
- pixels[i] = (unsigned char) (total / 2);
- }
- break;
- case 3:
- for (i=0; i <= safe_w; ++i) {
- total += pixels[i] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
- pixels[i] = (unsigned char) (total / 3);
- }
- break;
- case 4:
- for (i=0; i <= safe_w; ++i) {
- total += pixels[i] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
- pixels[i] = (unsigned char) (total / 4);
- }
- break;
- case 5:
- for (i=0; i <= safe_w; ++i) {
- total += pixels[i] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
- pixels[i] = (unsigned char) (total / 5);
- }
- break;
- default:
- for (i=0; i <= safe_w; ++i) {
- total += pixels[i] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
- pixels[i] = (unsigned char) (total / kernel_width);
- }
- break;
- }
-
- for (; i < w; ++i) {
- STBTT_assert(pixels[i] == 0);
- total -= buffer[i & STBTT__OVER_MASK];
- pixels[i] = (unsigned char) (total / kernel_width);
- }
-
- pixels += stride_in_bytes;
- }
-}
-
-static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
-{
- unsigned char buffer[STBTT_MAX_OVERSAMPLE];
- int safe_h = h - kernel_width;
- int j;
- STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
- for (j=0; j < w; ++j) {
- int i;
- unsigned int total;
- STBTT_memset(buffer, 0, kernel_width);
-
- total = 0;
-
- // make kernel_width a constant in common cases so compiler can optimize out the divide
- switch (kernel_width) {
- case 2:
- for (i=0; i <= safe_h; ++i) {
- total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
- pixels[i*stride_in_bytes] = (unsigned char) (total / 2);
- }
- break;
- case 3:
- for (i=0; i <= safe_h; ++i) {
- total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
- pixels[i*stride_in_bytes] = (unsigned char) (total / 3);
- }
- break;
- case 4:
- for (i=0; i <= safe_h; ++i) {
- total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
- pixels[i*stride_in_bytes] = (unsigned char) (total / 4);
- }
- break;
- case 5:
- for (i=0; i <= safe_h; ++i) {
- total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
- pixels[i*stride_in_bytes] = (unsigned char) (total / 5);
- }
- break;
- default:
- for (i=0; i <= safe_h; ++i) {
- total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
- buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
- pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
- }
- break;
- }
-
- for (; i < h; ++i) {
- STBTT_assert(pixels[i*stride_in_bytes] == 0);
- total -= buffer[i & STBTT__OVER_MASK];
- pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
- }
-
- pixels += 1;
- }
-}
-
-static float stbtt__oversample_shift(int oversample)
-{
- if (!oversample)
- return 0.0f;
-
- // The prefilter is a box filter of width "oversample",
- // which shifts phase by (oversample - 1)/2 pixels in
- // oversampled space. We want to shift in the opposite
- // direction to counter this.
- return (float)-(oversample - 1) / (2.0f * (float)oversample);
-}
-
-// rects array must be big enough to accommodate all characters in the given ranges
-STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
-{
- int i,j,k;
- int missing_glyph_added = 0;
-
- k=0;
- for (i=0; i < num_ranges; ++i) {
- float fh = ranges[i].font_size;
- float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
- ranges[i].h_oversample = (unsigned char) spc->h_oversample;
- ranges[i].v_oversample = (unsigned char) spc->v_oversample;
- for (j=0; j < ranges[i].num_chars; ++j) {
- int x0,y0,x1,y1;
- int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
- int glyph = stbtt_FindGlyphIndex(info, codepoint);
- if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) {
- rects[k].w = rects[k].h = 0;
- } else {
- stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,
- scale * spc->h_oversample,
- scale * spc->v_oversample,
- 0,0,
- &x0,&y0,&x1,&y1);
- rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
- rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
- if (glyph == 0)
- missing_glyph_added = 1;
- }
- ++k;
- }
- }
-
- return k;
-}
-
-STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph)
-{
- stbtt_MakeGlyphBitmapSubpixel(info,
- output,
- out_w - (prefilter_x - 1),
- out_h - (prefilter_y - 1),
- out_stride,
- scale_x,
- scale_y,
- shift_x,
- shift_y,
- glyph);
-
- if (prefilter_x > 1)
- stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x);
-
- if (prefilter_y > 1)
- stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y);
-
- *sub_x = stbtt__oversample_shift(prefilter_x);
- *sub_y = stbtt__oversample_shift(prefilter_y);
-}
-
-// rects array must be big enough to accommodate all characters in the given ranges
-STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
-{
- int i,j,k, missing_glyph = -1, return_value = 1;
-
- // save current values
- int old_h_over = spc->h_oversample;
- int old_v_over = spc->v_oversample;
-
- k = 0;
- for (i=0; i < num_ranges; ++i) {
- float fh = ranges[i].font_size;
- float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
- float recip_h,recip_v,sub_x,sub_y;
- spc->h_oversample = ranges[i].h_oversample;
- spc->v_oversample = ranges[i].v_oversample;
- recip_h = 1.0f / spc->h_oversample;
- recip_v = 1.0f / spc->v_oversample;
- sub_x = stbtt__oversample_shift(spc->h_oversample);
- sub_y = stbtt__oversample_shift(spc->v_oversample);
- for (j=0; j < ranges[i].num_chars; ++j) {
- stbrp_rect *r = &rects[k];
- if (r->was_packed && r->w != 0 && r->h != 0) {
- stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
- int advance, lsb, x0,y0,x1,y1;
- int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
- int glyph = stbtt_FindGlyphIndex(info, codepoint);
- stbrp_coord pad = (stbrp_coord) spc->padding;
-
- // pad on left and top
- r->x += pad;
- r->y += pad;
- r->w -= pad;
- r->h -= pad;
- stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb);
- stbtt_GetGlyphBitmapBox(info, glyph,
- scale * spc->h_oversample,
- scale * spc->v_oversample,
- &x0,&y0,&x1,&y1);
- stbtt_MakeGlyphBitmapSubpixel(info,
- spc->pixels + r->x + r->y*spc->stride_in_bytes,
- r->w - spc->h_oversample+1,
- r->h - spc->v_oversample+1,
- spc->stride_in_bytes,
- scale * spc->h_oversample,
- scale * spc->v_oversample,
- 0,0,
- glyph);
-
- if (spc->h_oversample > 1)
- stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
- r->w, r->h, spc->stride_in_bytes,
- spc->h_oversample);
-
- if (spc->v_oversample > 1)
- stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
- r->w, r->h, spc->stride_in_bytes,
- spc->v_oversample);
-
- bc->x0 = (stbtt_int16) r->x;
- bc->y0 = (stbtt_int16) r->y;
- bc->x1 = (stbtt_int16) (r->x + r->w);
- bc->y1 = (stbtt_int16) (r->y + r->h);
- bc->xadvance = scale * advance;
- bc->xoff = (float) x0 * recip_h + sub_x;
- bc->yoff = (float) y0 * recip_v + sub_y;
- bc->xoff2 = (x0 + r->w) * recip_h + sub_x;
- bc->yoff2 = (y0 + r->h) * recip_v + sub_y;
-
- if (glyph == 0)
- missing_glyph = j;
- } else if (spc->skip_missing) {
- return_value = 0;
- } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) {
- ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph];
- } else {
- return_value = 0; // if any fail, report failure
- }
-
- ++k;
- }
- }
-
- // restore original values
- spc->h_oversample = old_h_over;
- spc->v_oversample = old_v_over;
-
- return return_value;
-}
-
-STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects)
-{
- stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects);
-}
-
-STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
-{
- stbtt_fontinfo info;
- int i,j,n, return_value = 1;
- //stbrp_context *context = (stbrp_context *) spc->pack_info;
- stbrp_rect *rects;
-
- // flag all characters as NOT packed
- for (i=0; i < num_ranges; ++i)
- for (j=0; j < ranges[i].num_chars; ++j)
- ranges[i].chardata_for_range[j].x0 =
- ranges[i].chardata_for_range[j].y0 =
- ranges[i].chardata_for_range[j].x1 =
- ranges[i].chardata_for_range[j].y1 = 0;
-
- n = 0;
- for (i=0; i < num_ranges; ++i)
- n += ranges[i].num_chars;
-
- rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);
- if (rects == NULL)
- return 0;
-
- info.userdata = spc->user_allocator_context;
- stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));
-
- n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);
-
- stbtt_PackFontRangesPackRects(spc, rects, n);
-
- return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);
-
- STBTT_free(rects, spc->user_allocator_context);
- return return_value;
-}
-
-STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size,
- int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)
-{
- stbtt_pack_range range;
- range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range;
- range.array_of_unicode_codepoints = NULL;
- range.num_chars = num_chars_in_range;
- range.chardata_for_range = chardata_for_range;
- range.font_size = font_size;
- return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);
-}
-
-STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap)
-{
- int i_ascent, i_descent, i_lineGap;
- float scale;
- stbtt_fontinfo info;
- stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index));
- scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size);
- stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap);
- *ascent = (float) i_ascent * scale;
- *descent = (float) i_descent * scale;
- *lineGap = (float) i_lineGap * scale;
-}
-
-STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
-{
- float ipw = 1.0f / pw, iph = 1.0f / ph;
- const stbtt_packedchar *b = chardata + char_index;
-
- if (align_to_integer) {
- float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);
- float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f);
- q->x0 = x;
- q->y0 = y;
- q->x1 = x + b->xoff2 - b->xoff;
- q->y1 = y + b->yoff2 - b->yoff;
- } else {
- q->x0 = *xpos + b->xoff;
- q->y0 = *ypos + b->yoff;
- q->x1 = *xpos + b->xoff2;
- q->y1 = *ypos + b->yoff2;
- }
-
- q->s0 = b->x0 * ipw;
- q->t0 = b->y0 * iph;
- q->s1 = b->x1 * ipw;
- q->t1 = b->y1 * iph;
-
- *xpos += b->xadvance;
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// sdf computation
-//
-
-#define STBTT_min(a,b) ((a) < (b) ? (a) : (b))
-#define STBTT_max(a,b) ((a) < (b) ? (b) : (a))
-
-static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2])
-{
- float q0perp = q0[1]*ray[0] - q0[0]*ray[1];
- float q1perp = q1[1]*ray[0] - q1[0]*ray[1];
- float q2perp = q2[1]*ray[0] - q2[0]*ray[1];
- float roperp = orig[1]*ray[0] - orig[0]*ray[1];
-
- float a = q0perp - 2*q1perp + q2perp;
- float b = q1perp - q0perp;
- float c = q0perp - roperp;
-
- float s0 = 0., s1 = 0.;
- int num_s = 0;
-
- if (a != 0.0) {
- float discr = b*b - a*c;
- if (discr > 0.0) {
- float rcpna = -1 / a;
- float d = (float) STBTT_sqrt(discr);
- s0 = (b+d) * rcpna;
- s1 = (b-d) * rcpna;
- if (s0 >= 0.0 && s0 <= 1.0)
- num_s = 1;
- if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) {
- if (num_s == 0) s0 = s1;
- ++num_s;
- }
- }
- } else {
- // 2*b*s + c = 0
- // s = -c / (2*b)
- s0 = c / (-2 * b);
- if (s0 >= 0.0 && s0 <= 1.0)
- num_s = 1;
- }
-
- if (num_s == 0)
- return 0;
- else {
- float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]);
- float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2;
-
- float q0d = q0[0]*rayn_x + q0[1]*rayn_y;
- float q1d = q1[0]*rayn_x + q1[1]*rayn_y;
- float q2d = q2[0]*rayn_x + q2[1]*rayn_y;
- float rod = orig[0]*rayn_x + orig[1]*rayn_y;
-
- float q10d = q1d - q0d;
- float q20d = q2d - q0d;
- float q0rd = q0d - rod;
-
- hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d;
- hits[0][1] = a*s0+b;
-
- if (num_s > 1) {
- hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d;
- hits[1][1] = a*s1+b;
- return 2;
- } else {
- return 1;
- }
- }
-}
-
-static int equal(float *a, float *b)
-{
- return (a[0] == b[0] && a[1] == b[1]);
-}
-
-static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts)
-{
- int i;
- float orig[2], ray[2] = { 1, 0 };
- float y_frac;
- int winding = 0;
-
- // make sure y never passes through a vertex of the shape
- y_frac = (float) STBTT_fmod(y, 1.0f);
- if (y_frac < 0.01f)
- y += 0.01f;
- else if (y_frac > 0.99f)
- y -= 0.01f;
-
- orig[0] = x;
- orig[1] = y;
-
- // test a ray from (-infinity,y) to (x,y)
- for (i=0; i < nverts; ++i) {
- if (verts[i].type == STBTT_vline) {
- int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y;
- int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y;
- if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
- float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
- if (x_inter < x)
- winding += (y0 < y1) ? 1 : -1;
- }
- }
- if (verts[i].type == STBTT_vcurve) {
- int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ;
- int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy;
- int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ;
- int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2));
- int by = STBTT_max(y0,STBTT_max(y1,y2));
- if (y > ay && y < by && x > ax) {
- float q0[2],q1[2],q2[2];
- float hits[2][2];
- q0[0] = (float)x0;
- q0[1] = (float)y0;
- q1[0] = (float)x1;
- q1[1] = (float)y1;
- q2[0] = (float)x2;
- q2[1] = (float)y2;
- if (equal(q0,q1) || equal(q1,q2)) {
- x0 = (int)verts[i-1].x;
- y0 = (int)verts[i-1].y;
- x1 = (int)verts[i ].x;
- y1 = (int)verts[i ].y;
- if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
- float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0;
- if (x_inter < x)
- winding += (y0 < y1) ? 1 : -1;
- }
- } else {
- int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits);
- if (num_hits >= 1)
- if (hits[0][0] < 0)
- winding += (hits[0][1] < 0 ? -1 : 1);
- if (num_hits >= 2)
- if (hits[1][0] < 0)
- winding += (hits[1][1] < 0 ? -1 : 1);
- }
- }
- }
- }
- return winding;
-}
-
-static float stbtt__cuberoot( float x )
-{
- if (x<0)
- return -(float) STBTT_pow(-x,1.0f/3.0f);
- else
- return (float) STBTT_pow( x,1.0f/3.0f);
-}
-
-// x^3 + a*x^2 + b*x + c = 0
-static int stbtt__solve_cubic(float a, float b, float c, float* r)
-{
- float s = -a / 3;
- float p = b - a*a / 3;
- float q = a * (2*a*a - 9*b) / 27 + c;
- float p3 = p*p*p;
- float d = q*q + 4*p3 / 27;
- if (d >= 0) {
- float z = (float) STBTT_sqrt(d);
- float u = (-q + z) / 2;
- float v = (-q - z) / 2;
- u = stbtt__cuberoot(u);
- v = stbtt__cuberoot(v);
- r[0] = s + u + v;
- return 1;
- } else {
- float u = (float) STBTT_sqrt(-p/3);
- float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative
- float m = (float) STBTT_cos(v);
- float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f;
- r[0] = s + u * 2 * m;
- r[1] = s - u * (m + n);
- r[2] = s - u * (m - n);
-
- //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe?
- //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f);
- //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f);
- return 3;
- }
-}
-
-STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)
-{
- float scale_x = scale, scale_y = scale;
- int ix0,iy0,ix1,iy1;
- int w,h;
- unsigned char *data;
-
- if (scale == 0) return NULL;
-
- stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1);
-
- // if empty, return NULL
- if (ix0 == ix1 || iy0 == iy1)
- return NULL;
-
- ix0 -= padding;
- iy0 -= padding;
- ix1 += padding;
- iy1 += padding;
-
- w = (ix1 - ix0);
- h = (iy1 - iy0);
-
- if (width ) *width = w;
- if (height) *height = h;
- if (xoff ) *xoff = ix0;
- if (yoff ) *yoff = iy0;
-
- // invert for y-downwards bitmaps
- scale_y = -scale_y;
-
- {
- int x,y,i,j;
- float *precompute;
- stbtt_vertex *verts;
- int num_verts = stbtt_GetGlyphShape(info, glyph, &verts);
- data = (unsigned char *) STBTT_malloc(w * h, info->userdata);
- precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata);
-
- for (i=0,j=num_verts-1; i < num_verts; j=i++) {
- if (verts[i].type == STBTT_vline) {
- float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
- float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y;
- float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
- precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist;
- } else if (verts[i].type == STBTT_vcurve) {
- float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y;
- float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y;
- float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y;
- float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
- float len2 = bx*bx + by*by;
- if (len2 != 0.0f)
- precompute[i] = 1.0f / (bx*bx + by*by);
- else
- precompute[i] = 0.0f;
- } else
- precompute[i] = 0.0f;
- }
-
- for (y=iy0; y < iy1; ++y) {
- for (x=ix0; x < ix1; ++x) {
- float val;
- float min_dist = 999999.0f;
- float sx = (float) x + 0.5f;
- float sy = (float) y + 0.5f;
- float x_gspace = (sx / scale_x);
- float y_gspace = (sy / scale_y);
-
- int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path
-
- for (i=0; i < num_verts; ++i) {
- float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y;
-
- if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) {
- float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y;
-
- float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
- if (dist2 < min_dist*min_dist)
- min_dist = (float) STBTT_sqrt(dist2);
-
- // coarse culling against bbox
- //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist &&
- // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist)
- dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i];
- STBTT_assert(i != 0);
- if (dist < min_dist) {
- // check position along line
- // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0)
- // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy)
- float dx = x1-x0, dy = y1-y0;
- float px = x0-sx, py = y0-sy;
- // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy
- // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve
- float t = -(px*dx + py*dy) / (dx*dx + dy*dy);
- if (t >= 0.0f && t <= 1.0f)
- min_dist = dist;
- }
- } else if (verts[i].type == STBTT_vcurve) {
- float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y;
- float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y;
- float box_x0 = STBTT_min(STBTT_min(x0,x1),x2);
- float box_y0 = STBTT_min(STBTT_min(y0,y1),y2);
- float box_x1 = STBTT_max(STBTT_max(x0,x1),x2);
- float box_y1 = STBTT_max(STBTT_max(y0,y1),y2);
- // coarse culling against bbox to avoid computing cubic unnecessarily
- if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) {
- int num=0;
- float ax = x1-x0, ay = y1-y0;
- float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2;
- float mx = x0 - sx, my = y0 - sy;
- float res[3] = {0.f,0.f,0.f};
- float px,py,t,it,dist2;
- float a_inv = precompute[i];
- if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula
- float a = 3*(ax*bx + ay*by);
- float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by);
- float c = mx*ax+my*ay;
- if (a == 0.0) { // if a is 0, it's linear
- if (b != 0.0) {
- res[num++] = -c/b;
- }
- } else {
- float discriminant = b*b - 4*a*c;
- if (discriminant < 0)
- num = 0;
- else {
- float root = (float) STBTT_sqrt(discriminant);
- res[0] = (-b - root)/(2*a);
- res[1] = (-b + root)/(2*a);
- num = 2; // don't bother distinguishing 1-solution case, as code below will still work
- }
- }
- } else {
- float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point
- float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv;
- float d = (mx*ax+my*ay) * a_inv;
- num = stbtt__solve_cubic(b, c, d, res);
- }
- dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy);
- if (dist2 < min_dist*min_dist)
- min_dist = (float) STBTT_sqrt(dist2);
-
- if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) {
- t = res[0], it = 1.0f - t;
- px = it*it*x0 + 2*t*it*x1 + t*t*x2;
- py = it*it*y0 + 2*t*it*y1 + t*t*y2;
- dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
- if (dist2 < min_dist * min_dist)
- min_dist = (float) STBTT_sqrt(dist2);
- }
- if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) {
- t = res[1], it = 1.0f - t;
- px = it*it*x0 + 2*t*it*x1 + t*t*x2;
- py = it*it*y0 + 2*t*it*y1 + t*t*y2;
- dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
- if (dist2 < min_dist * min_dist)
- min_dist = (float) STBTT_sqrt(dist2);
- }
- if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) {
- t = res[2], it = 1.0f - t;
- px = it*it*x0 + 2*t*it*x1 + t*t*x2;
- py = it*it*y0 + 2*t*it*y1 + t*t*y2;
- dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy);
- if (dist2 < min_dist * min_dist)
- min_dist = (float) STBTT_sqrt(dist2);
- }
- }
- }
- }
- if (winding == 0)
- min_dist = -min_dist; // if outside the shape, value is negative
- val = onedge_value + pixel_dist_scale * min_dist;
- if (val < 0)
- val = 0;
- else if (val > 255)
- val = 255;
- data[(y-iy0)*w+(x-ix0)] = (unsigned char) val;
- }
- }
- STBTT_free(precompute, info->userdata);
- STBTT_free(verts, info->userdata);
- }
- return data;
-}
-
-STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff)
-{
- return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff);
-}
-
-STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata)
-{
- STBTT_free(bitmap, userdata);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-//
-// font name matching -- recommended not to use this
-//
-
-// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string
-static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)
-{
- stbtt_int32 i=0;
-
- // convert utf16 to utf8 and compare the results while converting
- while (len2) {
- stbtt_uint16 ch = s2[0]*256 + s2[1];
- if (ch < 0x80) {
- if (i >= len1) return -1;
- if (s1[i++] != ch) return -1;
- } else if (ch < 0x800) {
- if (i+1 >= len1) return -1;
- if (s1[i++] != 0xc0 + (ch >> 6)) return -1;
- if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;
- } else if (ch >= 0xd800 && ch < 0xdc00) {
- stbtt_uint32 c;
- stbtt_uint16 ch2 = s2[2]*256 + s2[3];
- if (i+3 >= len1) return -1;
- c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;
- if (s1[i++] != 0xf0 + (c >> 18)) return -1;
- if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;
- if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1;
- if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1;
- s2 += 2; // plus another 2 below
- len2 -= 2;
- } else if (ch >= 0xdc00 && ch < 0xe000) {
- return -1;
- } else {
- if (i+2 >= len1) return -1;
- if (s1[i++] != 0xe0 + (ch >> 12)) return -1;
- if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;
- if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1;
- }
- s2 += 2;
- len2 -= 2;
- }
- return i;
-}
-
-static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2)
-{
- return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);
-}
-
-// returns results in whatever encoding you request... but note that 2-byte encodings
-// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare
-STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)
-{
- stbtt_int32 i,count,stringOffset;
- stbtt_uint8 *fc = font->data;
- stbtt_uint32 offset = font->fontstart;
- stbtt_uint32 nm = stbtt__find_table(fc, offset, "name");
- if (!nm) return NULL;
-
- count = ttUSHORT(fc+nm+2);
- stringOffset = nm + ttUSHORT(fc+nm+4);
- for (i=0; i < count; ++i) {
- stbtt_uint32 loc = nm + 6 + 12 * i;
- if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)
- && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {
- *length = ttUSHORT(fc+loc+8);
- return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10));
- }
- }
- return NULL;
-}
-
-static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)
-{
- stbtt_int32 i;
- stbtt_int32 count = ttUSHORT(fc+nm+2);
- stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);
-
- for (i=0; i < count; ++i) {
- stbtt_uint32 loc = nm + 6 + 12 * i;
- stbtt_int32 id = ttUSHORT(fc+loc+6);
- if (id == target_id) {
- // find the encoding
- stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);
-
- // is this a Unicode encoding?
- if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {
- stbtt_int32 slen = ttUSHORT(fc+loc+8);
- stbtt_int32 off = ttUSHORT(fc+loc+10);
-
- // check if there's a prefix match
- stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);
- if (matchlen >= 0) {
- // check for target_id+1 immediately following, with same encoding & language
- if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {
- slen = ttUSHORT(fc+loc+12+8);
- off = ttUSHORT(fc+loc+12+10);
- if (slen == 0) {
- if (matchlen == nlen)
- return 1;
- } else if (matchlen < nlen && name[matchlen] == ' ') {
- ++matchlen;
- if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))
- return 1;
- }
- } else {
- // if nothing immediately following
- if (matchlen == nlen)
- return 1;
- }
- }
- }
-
- // @TODO handle other encodings
- }
- }
- return 0;
-}
-
-static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)
-{
- stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name);
- stbtt_uint32 nm,hd;
- if (!stbtt__isfont(fc+offset)) return 0;
-
- // check italics/bold/underline flags in macStyle...
- if (flags) {
- hd = stbtt__find_table(fc, offset, "head");
- if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;
- }
-
- nm = stbtt__find_table(fc, offset, "name");
- if (!nm) return 0;
-
- if (flags) {
- // if we checked the macStyle flags, then just check the family and ignore the subfamily
- if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1;
- if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1;
- if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
- } else {
- if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1;
- if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1;
- if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
- }
-
- return 0;
-}
-
-static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags)
-{
- stbtt_int32 i;
- for (i=0;;++i) {
- stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);
- if (off < 0) return off;
- if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))
- return off;
- }
-}
-
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wcast-qual"
-#endif
-
-STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,
- float pixel_height, unsigned char *pixels, int pw, int ph,
- int first_char, int num_chars, stbtt_bakedchar *chardata)
-{
- return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata);
-}
-
-STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index)
-{
- return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index);
-}
-
-STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data)
-{
- return stbtt_GetNumberOfFonts_internal((unsigned char *) data);
-}
-
-STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset)
-{
- return stbtt_InitFont_internal(info, (unsigned char *) data, offset);
-}
-
-STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags)
-{
- return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags);
-}
-
-STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
-{
- return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2);
-}
-
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic pop
-#endif
-
-#endif // STB_TRUETYPE_IMPLEMENTATION
-
-
-// FULL VERSION HISTORY
-//
-// 1.25 (2021-07-11) many fixes
-// 1.24 (2020-02-05) fix warning
-// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
-// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
-// 1.21 (2019-02-25) fix warning
-// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()
-// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod
-// 1.18 (2018-01-29) add missing function
-// 1.17 (2017-07-23) make more arguments const; doc fix
-// 1.16 (2017-07-12) SDF support
-// 1.15 (2017-03-03) make more arguments const
-// 1.14 (2017-01-16) num-fonts-in-TTC function
-// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts
-// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
-// 1.11 (2016-04-02) fix unused-variable warning
-// 1.10 (2016-04-02) allow user-defined fabs() replacement
-// fix memory leak if fontsize=0.0
-// fix warning from duplicate typedef
-// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges
-// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
-// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
-// allow PackFontRanges to pack and render in separate phases;
-// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
-// fixed an assert() bug in the new rasterizer
-// replace assert() with STBTT_assert() in new rasterizer
-// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)
-// also more precise AA rasterizer, except if shapes overlap
-// remove need for STBTT_sort
-// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC
-// 1.04 (2015-04-15) typo in example
-// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes
-// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++
-// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match
-// non-oversampled; STBTT_POINT_SIZE for packed case only
-// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling
-// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg)
-// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID
-// 0.8b (2014-07-07) fix a warning
-// 0.8 (2014-05-25) fix a few more warnings
-// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back
-// 0.6c (2012-07-24) improve documentation
-// 0.6b (2012-07-20) fix a few more warnings
-// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels,
-// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty
-// 0.5 (2011-12-09) bugfixes:
-// subpixel glyph renderer computed wrong bounding box
-// first vertex of shape can be off-curve (FreeSans)
-// 0.4b (2011-12-03) fixed an error in the font baking example
-// 0.4 (2011-12-01) kerning, subpixel rendering (tor)
-// bugfixes for:
-// codepoint-to-glyph conversion using table fmt=12
-// codepoint-to-glyph conversion using table fmt=4
-// stbtt_GetBakedQuad with non-square texture (Zer)
-// updated Hello World! sample to use kerning and subpixel
-// fixed some warnings
-// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM)
-// userdata, malloc-from-userdata, non-zero fill (stb)
-// 0.2 (2009-03-11) Fix unsigned/signed char warnings
-// 0.1 (2009-03-09) First public release
-//
-
-/*
-------------------------------------------------------------------------------
-This software is available under 2 licenses -- choose whichever you prefer.
-------------------------------------------------------------------------------
-ALTERNATIVE A - MIT License
-Copyright (c) 2017 Sean Barrett
-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.
-------------------------------------------------------------------------------
-ALTERNATIVE B - Public Domain (www.unlicense.org)
-This is free and unencumbered software released into the public domain.
-Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
-software, either in source code form or as a compiled binary, for any purpose,
-commercial or non-commercial, and by any means.
-In jurisdictions that recognize copyright laws, the author or authors of this
-software dedicate any and all copyright interest in the software to the public
-domain. We make this dedication for the benefit of the public at large and to
-the detriment of our heirs and successors. We intend this dedication to be an
-overt act of relinquishment in perpetuity of all present and future rights to
-this software under copyright law.
-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 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.
-------------------------------------------------------------------------------
-*/