From e7043f3bd262a4392cafb9c38b928696627d5124 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 29 Jul 2022 16:24:15 +0200 Subject: [PATCH 01/18] [sdl] Initial scafold. --- CMakeLists.txt | 16 +++++++++++++--- spine-sdl/CMakeLists.txt | 32 ++++++++++++++++++++++++++++++++ spine-sdl/example/main.c | 3 +++ spine-sdl/example/main.cpp | 22 ++++++++++++++++++++++ spine-sdl/src/spine-sdl-c.c | 0 spine-sdl/src/spine-sdl-c.h | 0 spine-sdl/src/spine-sdl-cpp.cpp | 0 spine-sdl/src/spine-sdl-cpp.h | 0 8 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 spine-sdl/CMakeLists.txt create mode 100644 spine-sdl/example/main.c create mode 100644 spine-sdl/example/main.cpp create mode 100644 spine-sdl/src/spine-sdl-c.c create mode 100644 spine-sdl/src/spine-sdl-c.h create mode 100644 spine-sdl/src/spine-sdl-cpp.cpp create mode 100644 spine-sdl/src/spine-sdl-cpp.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7617a9239..7e2e1ff5b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,8 +11,8 @@ if(MSVC) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") set (CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS}") else() - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wunused-value -Wno-c++11-long-long -Wno-variadic-macros -Werror -Wextra -pedantic -Wnonportable-include-path -Wshadow -std=c89") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wunused-value -Wno-c++11-long-long -Wno-variadic-macros -Werror -Wextra -Wnon-virtual-dtor -pedantic -Wnonportable-include-path -Wshadow -std=c++11 -fno-exceptions -fno-rtti") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wunused-value -Wno-c++11-long-long -Wno-variadic-macros -Wextra -pedantic -Wnonportable-include-path -Wshadow -std=c89") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wunused-value -Wno-c++11-long-long -Wno-variadic-macros -Wextra -Wnon-virtual-dtor -pedantic -Wnonportable-include-path -Wshadow -std=c++11 -fno-exceptions -fno-rtti") if (${SPINE_SANITIZE}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fsanitize=undefined") @@ -22,7 +22,7 @@ endif() if((${SPINE_SFML}) OR (${CMAKE_CURRENT_BINARY_DIR} MATCHES "spine-sfml")) if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(CMAKE_OSX_ARCHITECTURES x86_64) + set(CMAKE_OSX_ARCHITECTURES x86_64;arm64) set(ONLY_ACTIVE_ARCH NO) endif() add_subdirectory(spine-c) @@ -31,5 +31,15 @@ if((${SPINE_SFML}) OR (${CMAKE_CURRENT_BINARY_DIR} MATCHES "spine-sfml")) add_subdirectory(spine-sfml/cpp) endif() +if((${SPINE_SDL}) OR (${CMAKE_CURRENT_BINARY_DIR} MATCHES "spine-sdl")) + if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(CMAKE_OSX_ARCHITECTURES x86_64;arm64) + set(ONLY_ACTIVE_ARCH NO) + endif() + add_subdirectory(spine-c) + add_subdirectory(spine-cpp) + add_subdirectory(spine-sdl) +endif() + # add_subdirectory(spine-c/spine-c-unit-tests) add_subdirectory(spine-cpp/spine-cpp-unit-tests) diff --git a/spine-sdl/CMakeLists.txt b/spine-sdl/CMakeLists.txt new file mode 100644 index 000000000..a7a99e554 --- /dev/null +++ b/spine-sdl/CMakeLists.txt @@ -0,0 +1,32 @@ +if(MSVC) + message("MSCV detected") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_CRT_SECURE_NO_WARNINGS") +else() + message("${CMAKE_C_FLAGS}") + message("${CMAKE_CXX_FLAGS}") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -pedantic -Wno-unused-parameter -std=c99") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wnon-virtual-dtor -pedantic -Wno-unused-parameter -std=c++11 -fno-exceptions -fno-rtti") +endif() + +include(FetchContent) +FetchContent_Declare(SDL GIT_REPOSITORY https://github.com/libsdl-org/SDL GIT_TAG release-2.0.22) +FetchContent_MakeAvailable(SDL) + +include_directories(src) + +add_library(spine-sdl-c STATIC src/spine-sdl-c.c src/spine-sdl-c.h) +target_link_libraries(spine-sdl-c LINK_PUBLIC spine-c) +install(TARGETS spine-sdl-c DESTINATION dist/lib) +install(FILES src/spine-sdl-c.h DESTINATION dist/include) + +add_library(spine-sdl-cpp STATIC src/spine-sdl-cpp.cpp src/spine-sdl-cpp.h) +target_link_libraries(spine-sdl-cpp LINK_PUBLIC spine-cpp) +install(TARGETS spine-sdl-cpp DESTINATION dist/lib) +install(FILES src/spine-sdl-cpp.h DESTINATION dist/include) + +add_executable(spine-sdl-c-example example/main.c) +target_link_libraries(spine-sdl-c-example SDL2 spine-sdl-c) + +add_executable(spine-sdl-cpp-example example/main.cpp) +target_link_libraries(spine-sdl-cpp-example SDL2 spine-sdl-cpp) diff --git a/spine-sdl/example/main.c b/spine-sdl/example/main.c new file mode 100644 index 000000000..e9cdae165 --- /dev/null +++ b/spine-sdl/example/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} \ No newline at end of file diff --git a/spine-sdl/example/main.cpp b/spine-sdl/example/main.cpp new file mode 100644 index 000000000..01460b58a --- /dev/null +++ b/spine-sdl/example/main.cpp @@ -0,0 +1,22 @@ +#include +#include + +int main() { + SDL_Window *window = nullptr; + SDL_Surface *surface = nullptr; + + if (SDL_Init(SDL_INIT_VIDEO)) { + printf("Error: %s", SDL_GetError()); + return -1; + } + window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0); + if (!window) { + printf("Error: %s", SDL_GetError()); + return -1; + } + + + + SDL_Quit(); + return 0; +} \ No newline at end of file diff --git a/spine-sdl/src/spine-sdl-c.c b/spine-sdl/src/spine-sdl-c.c new file mode 100644 index 000000000..e69de29bb diff --git a/spine-sdl/src/spine-sdl-c.h b/spine-sdl/src/spine-sdl-c.h new file mode 100644 index 000000000..e69de29bb diff --git a/spine-sdl/src/spine-sdl-cpp.cpp b/spine-sdl/src/spine-sdl-cpp.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/spine-sdl/src/spine-sdl-cpp.h b/spine-sdl/src/spine-sdl-cpp.h new file mode 100644 index 000000000..e69de29bb From a03c92f7f263542b8c54ee89377541243a47c379 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 29 Jul 2022 17:01:27 +0200 Subject: [PATCH 02/18] [sdl] Add stb_image.h for image loading. --- spine-sdl/CMakeLists.txt | 8 +- spine-sdl/example/main.cpp | 81 +- spine-sdl/src/stb_image.h | 7897 ++++++++++++++++++++++++++++++++++++ 3 files changed, 7978 insertions(+), 8 deletions(-) create mode 100644 spine-sdl/src/stb_image.h diff --git a/spine-sdl/CMakeLists.txt b/spine-sdl/CMakeLists.txt index a7a99e554..3a677116d 100644 --- a/spine-sdl/CMakeLists.txt +++ b/spine-sdl/CMakeLists.txt @@ -15,15 +15,15 @@ FetchContent_MakeAvailable(SDL) include_directories(src) -add_library(spine-sdl-c STATIC src/spine-sdl-c.c src/spine-sdl-c.h) +add_library(spine-sdl-c STATIC src/spine-sdl-c.c src/spine-sdl-c.h src/stb_image.h) target_link_libraries(spine-sdl-c LINK_PUBLIC spine-c) install(TARGETS spine-sdl-c DESTINATION dist/lib) -install(FILES src/spine-sdl-c.h DESTINATION dist/include) +install(FILES src/spine-sdl-c.h src/stb_image.h DESTINATION dist/include) -add_library(spine-sdl-cpp STATIC src/spine-sdl-cpp.cpp src/spine-sdl-cpp.h) +add_library(spine-sdl-cpp STATIC src/spine-sdl-cpp.cpp src/spine-sdl-cpp.h src/stb_image.h) target_link_libraries(spine-sdl-cpp LINK_PUBLIC spine-cpp) install(TARGETS spine-sdl-cpp DESTINATION dist/lib) -install(FILES src/spine-sdl-cpp.h DESTINATION dist/include) +install(FILES src/spine-sdl-cpp.h src/stb_image.h DESTINATION dist/include) add_executable(spine-sdl-c-example example/main.c) target_link_libraries(spine-sdl-c-example SDL2 spine-sdl-c) diff --git a/spine-sdl/example/main.cpp b/spine-sdl/example/main.cpp index 01460b58a..03dde9d32 100644 --- a/spine-sdl/example/main.cpp +++ b/spine-sdl/example/main.cpp @@ -1,22 +1,95 @@ #include #include +#include + +#define STB_IMAGE_IMPLEMENTATION +#include + +namespace spine { + struct SkeletonDrawable { + Skeleton *skeleton; + bool ownsAnimationStateData; + AnimationState *animationState; + + SkeletonDrawable(SkeletonData *skeletonData, AnimationStateData *animationStateData = nullptr) { + Bone::setYDown(true); + skeleton = new (__FILE__, __LINE__) Skeleton(skeletonData); + + ownsAnimationStateData = animationStateData == 0; + if (ownsAnimationStateData) animationStateData = new (__FILE__, __LINE__) AnimationStateData(skeletonData); + animationState = new (__FILE__, __LINE__) AnimationState(animationStateData); + } + + ~SkeletonDrawable() { + if (ownsAnimationStateData) delete animationState->getData(); + delete animationState; + delete skeleton; + } + + void update(float delta) { + animationState->update(delta); + animationState->apply(*skeleton); + skeleton->updateWorldTransform(); + } + + void draw() { + + } + }; + + class SDLTextureLoader: public spine::TextureLoader { + void load(AtlasPage &page, const String &path) { + /*Texture *texture = new Texture(); + if (!texture->loadFromFile(path.buffer())) return; + + if (page.magFilter == TextureFilter_Linear) texture->setSmooth(true); + if (page.uWrap == TextureWrap_Repeat && page.vWrap == TextureWrap_Repeat) texture->setRepeated(true); + + page.setRendererObject(texture); + Vector2u size = texture->getSize(); + page.width = size.x; + page.height = size.y;*/ + } + + void unload(void *texture) { + // delete (Texture *) texture; + } + }; + + SpineExtension *getDefaultExtension() { + return new DefaultSpineExtension(); + } +} + +void loadSkeleton(const char *json, const char *skel, const char *atlas) { + +} int main() { - SDL_Window *window = nullptr; - SDL_Surface *surface = nullptr; - if (SDL_Init(SDL_INIT_VIDEO)) { printf("Error: %s", SDL_GetError()); return -1; } - window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0); + SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, + SDL_WINDOW_SHOWN); if (!window) { printf("Error: %s", SDL_GetError()); return -1; } + SDL_Surface *surface = SDL_GetWindowSurface(window); + spine::SkeletonDrawable skeletonDrawable(nullptr, nullptr); + bool exit = false; + do { + SDL_Event event; + while (SDL_PollEvent(&event) != 0) { + if (event.type == SDL_QUIT) + exit = true; + } + } while (!exit); + SDL_DestroyWindow(window); SDL_Quit(); return 0; } \ No newline at end of file diff --git a/spine-sdl/src/stb_image.h b/spine-sdl/src/stb_image.h new file mode 100644 index 000000000..5ba68ce2f --- /dev/null +++ b/spine-sdl/src/stb_image.h @@ -0,0 +1,7897 @@ +/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine Simon Breuss (16-bit PNM) + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko github:mosra + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + Jacko Dirks + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR +STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif +#endif + +#ifndef STBI_NO_HDR +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); +STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); +STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 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,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 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,0,0 }; + +static const int stbi__zdist_base[32] = { 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,0,0}; + +static const int stbi__zdist_extra[32] = +{ 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}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind( s ); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; + #endif + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +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. +------------------------------------------------------------------------------ +*/ \ No newline at end of file From 28a614177d937420843b7c8c68aa1a1b50023b64 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 29 Jul 2022 17:26:42 +0200 Subject: [PATCH 03/18] [sdl] Texture loader. --- spine-sdl/example/main.cpp | 80 +++++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/spine-sdl/example/main.cpp b/spine-sdl/example/main.cpp index 03dde9d32..efa4c6598 100644 --- a/spine-sdl/example/main.cpp +++ b/spine-sdl/example/main.cpp @@ -3,6 +3,7 @@ #include #define STB_IMAGE_IMPLEMENTATION + #include namespace spine { @@ -13,11 +14,11 @@ namespace spine { SkeletonDrawable(SkeletonData *skeletonData, AnimationStateData *animationStateData = nullptr) { Bone::setYDown(true); - skeleton = new (__FILE__, __LINE__) Skeleton(skeletonData); + skeleton = new(__FILE__, __LINE__) Skeleton(skeletonData); ownsAnimationStateData = animationStateData == 0; - if (ownsAnimationStateData) animationStateData = new (__FILE__, __LINE__) AnimationStateData(skeletonData); - animationState = new (__FILE__, __LINE__) AnimationState(animationStateData); + if (ownsAnimationStateData) animationStateData = new(__FILE__, __LINE__) AnimationStateData(skeletonData); + animationState = new(__FILE__, __LINE__) AnimationState(animationStateData); } ~SkeletonDrawable() { @@ -37,22 +38,41 @@ namespace spine { } }; - class SDLTextureLoader: public spine::TextureLoader { + class SDLTextureLoader : public spine::TextureLoader { + SDL_Renderer *renderer; + + SDLTextureLoader(SDL_Renderer *renderer): renderer(renderer) { + } + + SDL_Texture *loadTexture(SDL_Renderer *renderer, const spine::String &path) { + int width, height, components; + stbi_uc *imageData = stbi_load(path.buffer(), &width, &height, &components, 4); + if (!imageData) return nullptr; + SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height); + if (!texture) { + stbi_image_free(imageData); + return nullptr; + } + if (SDL_UpdateTexture(texture, nullptr, imageData, width * 4)) { + stbi_image_free(imageData); + return nullptr; + } + stbi_image_free(imageData); + return texture; + } + void load(AtlasPage &page, const String &path) { - /*Texture *texture = new Texture(); - if (!texture->loadFromFile(path.buffer())) return; - - if (page.magFilter == TextureFilter_Linear) texture->setSmooth(true); - if (page.uWrap == TextureWrap_Repeat && page.vWrap == TextureWrap_Repeat) texture->setRepeated(true); - + SDL_Texture *texture = loadTexture(renderer, path); + if (!texture) return; page.setRendererObject(texture); - Vector2u size = texture->getSize(); - page.width = size.x; - page.height = size.y;*/ + SDL_QueryTexture(texture, nullptr, nullptr, &page.width, &page.height); + + /*if (page.magFilter == TextureFilter_Linear) texture->setSmooth(true); + if (page.uWrap == TextureWrap_Repeat && page.vWrap == TextureWrap_Repeat) texture->setRepeated(true);*/ } void unload(void *texture) { - // delete (Texture *) texture; + SDL_DestroyTexture((SDL_Texture*)texture); } }; @@ -61,8 +81,8 @@ namespace spine { } } -void loadSkeleton(const char *json, const char *skel, const char *atlas) { - +spine::SkeletonDrawable *loadSkeleton(const char *json, const char *skel, const char *atlas) { + return nullptr; } int main() { @@ -70,24 +90,32 @@ int main() { printf("Error: %s", SDL_GetError()); return -1; } - SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, - SDL_WINDOW_SHOWN); + SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, 0); if (!window) { printf("Error: %s", SDL_GetError()); return -1; } - SDL_Surface *surface = SDL_GetWindowSurface(window); + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (!renderer) { + printf("Error: %s", SDL_GetError()); + return -1; + } + // spine::SkeletonDrawable skeletonDrawable(nullptr, nullptr); - spine::SkeletonDrawable skeletonDrawable(nullptr, nullptr); - - bool exit = false; - do { + bool quit = false; + while (!quit) { SDL_Event event; while (SDL_PollEvent(&event) != 0) { - if (event.type == SDL_QUIT) - exit = true; + if (event.type == SDL_QUIT) { + quit = true; + break; + } } - } while (!exit); + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); + } SDL_DestroyWindow(window); SDL_Quit(); From af9561de194440f047f06d360e48fef501ffaa22 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 29 Jul 2022 18:22:09 +0200 Subject: [PATCH 04/18] [sdl] Broken renderer. --- spine-sdl/example/main.cpp | 132 ++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 3 deletions(-) diff --git a/spine-sdl/example/main.cpp b/spine-sdl/example/main.cpp index efa4c6598..c1d50b384 100644 --- a/spine-sdl/example/main.cpp +++ b/spine-sdl/example/main.cpp @@ -11,6 +11,9 @@ namespace spine { Skeleton *skeleton; bool ownsAnimationStateData; AnimationState *animationState; + SkeletonClipping clipper; + Vector worldVertices; + Vector quadIndices; SkeletonDrawable(SkeletonData *skeletonData, AnimationStateData *animationStateData = nullptr) { Bone::setYDown(true); @@ -19,6 +22,12 @@ namespace spine { ownsAnimationStateData = animationStateData == 0; if (ownsAnimationStateData) animationStateData = new(__FILE__, __LINE__) AnimationStateData(skeletonData); animationState = new(__FILE__, __LINE__) AnimationState(animationStateData); + quadIndices.add(0); + quadIndices.add(1); + quadIndices.add(2); + quadIndices.add(2); + quadIndices.add(3); + quadIndices.add(0); } ~SkeletonDrawable() { @@ -33,18 +42,124 @@ namespace spine { skeleton->updateWorldTransform(); } - void draw() { + void draw(SDL_Renderer *renderer) { + SDL_Texture *texture; + spine::Vector sdlVertices; + SDL_Vertex sdlVertex; + Vector sdlIndices; + for (unsigned i = 0; i < skeleton->getSlots().size(); ++i) { + Slot &slot = *skeleton->getDrawOrder()[i]; + Attachment *attachment = slot.getAttachment(); + if (!attachment) continue; + // Early out if the slot color is 0 or the bone is not active + if (slot.getColor().a == 0 || !slot.getBone().isActive()) { + clipper.clipEnd(slot); + continue; + } + + Vector *vertices = &worldVertices; + int verticesCount = 0; + Vector *uvs = NULL; + Vector *indices; + int indicesCount = 0; + Color *attachmentColor; + + if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) { + RegionAttachment *regionAttachment = (RegionAttachment *) attachment; + attachmentColor = ®ionAttachment->getColor(); + + // Early out if the slot color is 0 + if (attachmentColor->a == 0) { + clipper.clipEnd(slot); + continue; + } + + worldVertices.setSize(8, 0); + regionAttachment->computeWorldVertices(slot, worldVertices, 0, 2); + verticesCount = 4; + uvs = ®ionAttachment->getUVs(); + indices = &quadIndices; + indicesCount = 6; + texture = (SDL_Texture *) ((AtlasRegion *) regionAttachment->getRendererObject())->page->getRendererObject(); + + } else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) { + MeshAttachment *mesh = (MeshAttachment *) attachment; + attachmentColor = &mesh->getColor(); + + // Early out if the slot color is 0 + if (attachmentColor->a == 0) { + clipper.clipEnd(slot); + continue; + } + + worldVertices.setSize(mesh->getWorldVerticesLength(), 0); + texture = (SDL_Texture *) ((AtlasRegion *) mesh->getRendererObject())->page->getRendererObject(); + mesh->computeWorldVertices(slot, 0, mesh->getWorldVerticesLength(), worldVertices.buffer(), 0, 2); + verticesCount = mesh->getWorldVerticesLength() >> 1; + uvs = &mesh->getUVs(); + indices = &mesh->getTriangles(); + indicesCount = indices->size(); + + } else if (attachment->getRTTI().isExactly(ClippingAttachment::rtti)) { + ClippingAttachment *clip = (ClippingAttachment *) slot.getAttachment(); + clipper.clipStart(slot, clip); + continue; + } else + continue; + + Uint8 r = static_cast(skeleton->getColor().r * slot.getColor().r * attachmentColor->r * 255); + Uint8 g = static_cast(skeleton->getColor().g * slot.getColor().g * attachmentColor->g * 255); + Uint8 b = static_cast(skeleton->getColor().b * slot.getColor().b * attachmentColor->b * 255); + Uint8 a = static_cast(skeleton->getColor().a * slot.getColor().a * attachmentColor->a * 255); + sdlVertex.color.r = r; + sdlVertex.color.g = g; + sdlVertex.color.b = b; + sdlVertex.color.a = a; + + Color light; + light.r = r / 255.0f; + light.g = g / 255.0f; + light.b = b / 255.0f; + light.a = a / 255.0f; + + if (clipper.isClipping()) { + clipper.clipTriangles(worldVertices, *indices, *uvs, 2); + vertices = &clipper.getClippedVertices(); + verticesCount = clipper.getClippedVertices().size() >> 1; + uvs = &clipper.getClippedUVs(); + indices = &clipper.getClippedTriangles(); + indicesCount = clipper.getClippedTriangles().size(); + } + + + sdlVertices.clear(); + for (int ii = 0; ii < verticesCount; ++ii) { + sdlVertex.position.x = (*vertices)[ii]; + sdlVertex.position.y = (*vertices)[ii + 1]; + sdlVertex.tex_coord.x = (*uvs)[ii]; + sdlVertex.tex_coord.y = (*uvs)[ii + 1]; + sdlVertices.add(sdlVertex); + } + sdlIndices.clear(); + for (int ii = 0; ii < indices->size(); ii++) + sdlIndices.add((*indices)[ii]); + + SDL_RenderGeometry(renderer, texture, sdlVertices.buffer(), sdlVertices.size(), sdlIndices.buffer(), indicesCount); + clipper.clipEnd(slot); + } + clipper.clipEnd(); } }; class SDLTextureLoader : public spine::TextureLoader { SDL_Renderer *renderer; + public: SDLTextureLoader(SDL_Renderer *renderer): renderer(renderer) { } - SDL_Texture *loadTexture(SDL_Renderer *renderer, const spine::String &path) { + SDL_Texture *loadTexture(const spine::String &path) { int width, height, components; stbi_uc *imageData = stbi_load(path.buffer(), &width, &height, &components, 4); if (!imageData) return nullptr; @@ -62,7 +177,7 @@ namespace spine { } void load(AtlasPage &page, const String &path) { - SDL_Texture *texture = loadTexture(renderer, path); + SDL_Texture *texture = loadTexture(path); if (!texture) return; page.setRendererObject(texture); SDL_QueryTexture(texture, nullptr, nullptr, &page.width, &page.height); @@ -100,6 +215,16 @@ int main() { printf("Error: %s", SDL_GetError()); return -1; } + + spine::SDLTextureLoader textureLoader(renderer); + spine::Atlas atlas("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas", &textureLoader); + spine::AtlasAttachmentLoader attachmentLoader(&atlas); + spine::SkeletonJson json(&attachmentLoader); + spine::SkeletonData *skeletonData = json.readSkeletonDataFile("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.json"); + spine::SkeletonDrawable drawable(skeletonData); + drawable.skeleton->setPosition(400, 500); + drawable.update(0); + // spine::SkeletonDrawable skeletonDrawable(nullptr, nullptr); bool quit = false; @@ -114,6 +239,7 @@ int main() { SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); + drawable.draw(renderer); SDL_RenderPresent(renderer); } From 5cd5a261868e815905033f68a5e05253d2c37ad2 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Mon, 1 Aug 2022 16:50:07 +0200 Subject: [PATCH 05/18] [sdl] C++ runtime complete. Unsupported: - premultiplied alpha - screen blend mode - two color tint --- formatters/build.gradle | 3 + spine-sdl/CMakeLists.txt | 2 +- spine-sdl/example/main.c | 2 +- spine-sdl/example/main.cpp | 290 +- spine-sdl/src/spine-sdl-cpp.cpp | 223 + spine-sdl/src/spine-sdl-cpp.h | 74 + spine-sdl/src/stb_image.h | 11294 +++++++++++++++--------------- 7 files changed, 6040 insertions(+), 5848 deletions(-) diff --git a/formatters/build.gradle b/formatters/build.gradle index bbdd39c97..e44ec4d7b 100644 --- a/formatters/build.gradle +++ b/formatters/build.gradle @@ -21,6 +21,9 @@ spotless { 'spine-cocos2dx/src/**/*.h', 'spine-cocos2dx/example/Classes/**/*.cpp', 'spine-cocos2dx/example/Classes/**/*.h', + 'spine-sdl/**/*.c', + 'spine-sdl/**/*.cpp', + 'spine-sdl/**/*.h', 'spine-sfml/**/*.c', 'spine-sfml/**/*.cpp', 'spine-sfml/**/*.h', diff --git a/spine-sdl/CMakeLists.txt b/spine-sdl/CMakeLists.txt index 3a677116d..bd84feea2 100644 --- a/spine-sdl/CMakeLists.txt +++ b/spine-sdl/CMakeLists.txt @@ -21,7 +21,7 @@ install(TARGETS spine-sdl-c DESTINATION dist/lib) install(FILES src/spine-sdl-c.h src/stb_image.h DESTINATION dist/include) add_library(spine-sdl-cpp STATIC src/spine-sdl-cpp.cpp src/spine-sdl-cpp.h src/stb_image.h) -target_link_libraries(spine-sdl-cpp LINK_PUBLIC spine-cpp) +target_link_libraries(spine-sdl-cpp LINK_PUBLIC SDL2 spine-cpp) install(TARGETS spine-sdl-cpp DESTINATION dist/lib) install(FILES src/spine-sdl-cpp.h src/stb_image.h DESTINATION dist/include) diff --git a/spine-sdl/example/main.c b/spine-sdl/example/main.c index e9cdae165..ffc6dabc2 100644 --- a/spine-sdl/example/main.c +++ b/spine-sdl/example/main.c @@ -1,3 +1,3 @@ int main() { - return 0; + return 0; } \ No newline at end of file diff --git a/spine-sdl/example/main.cpp b/spine-sdl/example/main.cpp index c1d50b384..7d4861679 100644 --- a/spine-sdl/example/main.cpp +++ b/spine-sdl/example/main.cpp @@ -1,249 +1,59 @@ -#include #include -#include - -#define STB_IMAGE_IMPLEMENTATION - -#include - -namespace spine { - struct SkeletonDrawable { - Skeleton *skeleton; - bool ownsAnimationStateData; - AnimationState *animationState; - SkeletonClipping clipper; - Vector worldVertices; - Vector quadIndices; - - SkeletonDrawable(SkeletonData *skeletonData, AnimationStateData *animationStateData = nullptr) { - Bone::setYDown(true); - skeleton = new(__FILE__, __LINE__) Skeleton(skeletonData); - - ownsAnimationStateData = animationStateData == 0; - if (ownsAnimationStateData) animationStateData = new(__FILE__, __LINE__) AnimationStateData(skeletonData); - animationState = new(__FILE__, __LINE__) AnimationState(animationStateData); - quadIndices.add(0); - quadIndices.add(1); - quadIndices.add(2); - quadIndices.add(2); - quadIndices.add(3); - quadIndices.add(0); - } - - ~SkeletonDrawable() { - if (ownsAnimationStateData) delete animationState->getData(); - delete animationState; - delete skeleton; - } - - void update(float delta) { - animationState->update(delta); - animationState->apply(*skeleton); - skeleton->updateWorldTransform(); - } - - void draw(SDL_Renderer *renderer) { - SDL_Texture *texture; - spine::Vector sdlVertices; - SDL_Vertex sdlVertex; - Vector sdlIndices; - for (unsigned i = 0; i < skeleton->getSlots().size(); ++i) { - Slot &slot = *skeleton->getDrawOrder()[i]; - Attachment *attachment = slot.getAttachment(); - if (!attachment) continue; - - // Early out if the slot color is 0 or the bone is not active - if (slot.getColor().a == 0 || !slot.getBone().isActive()) { - clipper.clipEnd(slot); - continue; - } - - Vector *vertices = &worldVertices; - int verticesCount = 0; - Vector *uvs = NULL; - Vector *indices; - int indicesCount = 0; - Color *attachmentColor; - - if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) { - RegionAttachment *regionAttachment = (RegionAttachment *) attachment; - attachmentColor = ®ionAttachment->getColor(); - - // Early out if the slot color is 0 - if (attachmentColor->a == 0) { - clipper.clipEnd(slot); - continue; - } - - worldVertices.setSize(8, 0); - regionAttachment->computeWorldVertices(slot, worldVertices, 0, 2); - verticesCount = 4; - uvs = ®ionAttachment->getUVs(); - indices = &quadIndices; - indicesCount = 6; - texture = (SDL_Texture *) ((AtlasRegion *) regionAttachment->getRendererObject())->page->getRendererObject(); - - } else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) { - MeshAttachment *mesh = (MeshAttachment *) attachment; - attachmentColor = &mesh->getColor(); - - // Early out if the slot color is 0 - if (attachmentColor->a == 0) { - clipper.clipEnd(slot); - continue; - } - - worldVertices.setSize(mesh->getWorldVerticesLength(), 0); - texture = (SDL_Texture *) ((AtlasRegion *) mesh->getRendererObject())->page->getRendererObject(); - mesh->computeWorldVertices(slot, 0, mesh->getWorldVerticesLength(), worldVertices.buffer(), 0, 2); - verticesCount = mesh->getWorldVerticesLength() >> 1; - uvs = &mesh->getUVs(); - indices = &mesh->getTriangles(); - indicesCount = indices->size(); - - } else if (attachment->getRTTI().isExactly(ClippingAttachment::rtti)) { - ClippingAttachment *clip = (ClippingAttachment *) slot.getAttachment(); - clipper.clipStart(slot, clip); - continue; - } else - continue; - - Uint8 r = static_cast(skeleton->getColor().r * slot.getColor().r * attachmentColor->r * 255); - Uint8 g = static_cast(skeleton->getColor().g * slot.getColor().g * attachmentColor->g * 255); - Uint8 b = static_cast(skeleton->getColor().b * slot.getColor().b * attachmentColor->b * 255); - Uint8 a = static_cast(skeleton->getColor().a * slot.getColor().a * attachmentColor->a * 255); - sdlVertex.color.r = r; - sdlVertex.color.g = g; - sdlVertex.color.b = b; - sdlVertex.color.a = a; - - Color light; - light.r = r / 255.0f; - light.g = g / 255.0f; - light.b = b / 255.0f; - light.a = a / 255.0f; - - if (clipper.isClipping()) { - clipper.clipTriangles(worldVertices, *indices, *uvs, 2); - vertices = &clipper.getClippedVertices(); - verticesCount = clipper.getClippedVertices().size() >> 1; - uvs = &clipper.getClippedUVs(); - indices = &clipper.getClippedTriangles(); - indicesCount = clipper.getClippedTriangles().size(); - } - - - sdlVertices.clear(); - for (int ii = 0; ii < verticesCount; ++ii) { - sdlVertex.position.x = (*vertices)[ii]; - sdlVertex.position.y = (*vertices)[ii + 1]; - sdlVertex.tex_coord.x = (*uvs)[ii]; - sdlVertex.tex_coord.y = (*uvs)[ii + 1]; - sdlVertices.add(sdlVertex); - } - sdlIndices.clear(); - for (int ii = 0; ii < indices->size(); ii++) - sdlIndices.add((*indices)[ii]); - - SDL_RenderGeometry(renderer, texture, sdlVertices.buffer(), sdlVertices.size(), sdlIndices.buffer(), indicesCount); - clipper.clipEnd(slot); - } - clipper.clipEnd(); - } - }; - - class SDLTextureLoader : public spine::TextureLoader { - SDL_Renderer *renderer; - - public: - SDLTextureLoader(SDL_Renderer *renderer): renderer(renderer) { - } - - SDL_Texture *loadTexture(const spine::String &path) { - int width, height, components; - stbi_uc *imageData = stbi_load(path.buffer(), &width, &height, &components, 4); - if (!imageData) return nullptr; - SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height); - if (!texture) { - stbi_image_free(imageData); - return nullptr; - } - if (SDL_UpdateTexture(texture, nullptr, imageData, width * 4)) { - stbi_image_free(imageData); - return nullptr; - } - stbi_image_free(imageData); - return texture; - } - - void load(AtlasPage &page, const String &path) { - SDL_Texture *texture = loadTexture(path); - if (!texture) return; - page.setRendererObject(texture); - SDL_QueryTexture(texture, nullptr, nullptr, &page.width, &page.height); - - /*if (page.magFilter == TextureFilter_Linear) texture->setSmooth(true); - if (page.uWrap == TextureWrap_Repeat && page.vWrap == TextureWrap_Repeat) texture->setRepeated(true);*/ - } - - void unload(void *texture) { - SDL_DestroyTexture((SDL_Texture*)texture); - } - }; - - SpineExtension *getDefaultExtension() { - return new DefaultSpineExtension(); - } -} - -spine::SkeletonDrawable *loadSkeleton(const char *json, const char *skel, const char *atlas) { - return nullptr; -} +#include int main() { - if (SDL_Init(SDL_INIT_VIDEO)) { - printf("Error: %s", SDL_GetError()); - return -1; - } - SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, 0); - if (!window) { - printf("Error: %s", SDL_GetError()); - return -1; - } - SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); - if (!renderer) { - printf("Error: %s", SDL_GetError()); - return -1; - } + if (SDL_Init(SDL_INIT_VIDEO)) { + printf("Error: %s", SDL_GetError()); + return -1; + } + SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, 0); + if (!window) { + printf("Error: %s", SDL_GetError()); + return -1; + } + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + if (!renderer) { + printf("Error: %s", SDL_GetError()); + return -1; + } - spine::SDLTextureLoader textureLoader(renderer); - spine::Atlas atlas("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas", &textureLoader); - spine::AtlasAttachmentLoader attachmentLoader(&atlas); - spine::SkeletonJson json(&attachmentLoader); - spine::SkeletonData *skeletonData = json.readSkeletonDataFile("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.json"); - spine::SkeletonDrawable drawable(skeletonData); - drawable.skeleton->setPosition(400, 500); - drawable.update(0); + spine::SDLTextureLoader textureLoader(renderer); + spine::Atlas atlas("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas", &textureLoader); + spine::AtlasAttachmentLoader attachmentLoader(&atlas); + spine::SkeletonJson json(&attachmentLoader); + json.setScale(0.5f); + spine::SkeletonData *skeletonData = json.readSkeletonDataFile("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.json"); + spine::SkeletonDrawable drawable(skeletonData); + drawable.skeleton->setPosition(400, 500); + drawable.skeleton->setToSetupPose(); + drawable.update(0); + drawable.animationState->setAnimation(0, "run", true); - // spine::SkeletonDrawable skeletonDrawable(nullptr, nullptr); + bool quit = false; + uint64_t lastFrameTime = SDL_GetPerformanceCounter(); + while (!quit) { + SDL_Event event; + while (SDL_PollEvent(&event) != 0) { + if (event.type == SDL_QUIT) { + quit = true; + break; + } + } - bool quit = false; - while (!quit) { - SDL_Event event; - while (SDL_PollEvent(&event) != 0) { - if (event.type == SDL_QUIT) { - quit = true; - break; - } - } + SDL_SetRenderDrawColor(renderer, 94, 93, 96, 255); + SDL_RenderClear(renderer); - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); - SDL_RenderClear(renderer); - drawable.draw(renderer); - SDL_RenderPresent(renderer); - } + uint64_t now = SDL_GetPerformanceCounter(); + double deltaTime = (now - lastFrameTime) / (double) SDL_GetPerformanceFrequency(); + lastFrameTime = now; - SDL_DestroyWindow(window); - SDL_Quit(); - return 0; + drawable.update(deltaTime); + drawable.draw(renderer); + + SDL_RenderPresent(renderer); + } + + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; } \ No newline at end of file diff --git a/spine-sdl/src/spine-sdl-cpp.cpp b/spine-sdl/src/spine-sdl-cpp.cpp index e69de29bb..6a48c8e56 100644 --- a/spine-sdl/src/spine-sdl-cpp.cpp +++ b/spine-sdl/src/spine-sdl-cpp.cpp @@ -0,0 +1,223 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated September 24, 2021. Replaces all prior versions. + * + * Copyright (c) 2013-2021, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, + * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include "spine-sdl-cpp.h" +#include + +#define STB_IMAGE_IMPLEMENTATION + +#include + +using namespace spine; + +SkeletonDrawable::SkeletonDrawable(SkeletonData *skeletonData, AnimationStateData *animationStateData) { + Bone::setYDown(true); + skeleton = new (__FILE__, __LINE__) Skeleton(skeletonData); + + ownsAnimationStateData = animationStateData == 0; + if (ownsAnimationStateData) animationStateData = new (__FILE__, __LINE__) AnimationStateData(skeletonData); + animationState = new (__FILE__, __LINE__) AnimationState(animationStateData); + quadIndices.add(0); + quadIndices.add(1); + quadIndices.add(2); + quadIndices.add(2); + quadIndices.add(3); + quadIndices.add(0); +} + +SkeletonDrawable::~SkeletonDrawable() { + if (ownsAnimationStateData) delete animationState->getData(); + delete animationState; + delete skeleton; +} + +void SkeletonDrawable::update(float delta) { + animationState->update(delta); + animationState->apply(*skeleton); + skeleton->updateWorldTransform(); +} + +void SkeletonDrawable::draw(SDL_Renderer *renderer) { + SDL_Texture *texture; + SDL_Vertex sdlVertex; + for (unsigned i = 0; i < skeleton->getSlots().size(); ++i) { + Slot &slot = *skeleton->getDrawOrder()[i]; + Attachment *attachment = slot.getAttachment(); + if (!attachment) continue; + + // Early out if the slot color is 0 or the bone is not active + if (slot.getColor().a == 0 || !slot.getBone().isActive()) { + clipper.clipEnd(slot); + continue; + } + + Vector *vertices = &worldVertices; + int verticesCount = 0; + Vector *uvs = NULL; + Vector *indices; + int indicesCount = 0; + Color *attachmentColor; + + if (attachment->getRTTI().isExactly(RegionAttachment::rtti)) { + RegionAttachment *regionAttachment = (RegionAttachment *) attachment; + attachmentColor = ®ionAttachment->getColor(); + + // Early out if the slot color is 0 + if (attachmentColor->a == 0) { + clipper.clipEnd(slot); + continue; + } + + worldVertices.setSize(8, 0); + regionAttachment->computeWorldVertices(slot, worldVertices, 0, 2); + verticesCount = 4; + uvs = ®ionAttachment->getUVs(); + indices = &quadIndices; + indicesCount = 6; + texture = (SDL_Texture *) ((AtlasRegion *) regionAttachment->getRendererObject())->page->getRendererObject(); + + } else if (attachment->getRTTI().isExactly(MeshAttachment::rtti)) { + MeshAttachment *mesh = (MeshAttachment *) attachment; + attachmentColor = &mesh->getColor(); + + // Early out if the slot color is 0 + if (attachmentColor->a == 0) { + clipper.clipEnd(slot); + continue; + } + + worldVertices.setSize(mesh->getWorldVerticesLength(), 0); + texture = (SDL_Texture *) ((AtlasRegion *) mesh->getRendererObject())->page->getRendererObject(); + mesh->computeWorldVertices(slot, 0, mesh->getWorldVerticesLength(), worldVertices.buffer(), 0, 2); + verticesCount = mesh->getWorldVerticesLength() >> 1; + uvs = &mesh->getUVs(); + indices = &mesh->getTriangles(); + indicesCount = indices->size(); + + } else if (attachment->getRTTI().isExactly(ClippingAttachment::rtti)) { + ClippingAttachment *clip = (ClippingAttachment *) slot.getAttachment(); + clipper.clipStart(slot, clip); + continue; + } else + continue; + + Uint8 r = static_cast(skeleton->getColor().r * slot.getColor().r * attachmentColor->r * 255); + Uint8 g = static_cast(skeleton->getColor().g * slot.getColor().g * attachmentColor->g * 255); + Uint8 b = static_cast(skeleton->getColor().b * slot.getColor().b * attachmentColor->b * 255); + Uint8 a = static_cast(skeleton->getColor().a * slot.getColor().a * attachmentColor->a * 255); + sdlVertex.color.r = r; + sdlVertex.color.g = g; + sdlVertex.color.b = b; + sdlVertex.color.a = a; + + if (clipper.isClipping()) { + clipper.clipTriangles(worldVertices, *indices, *uvs, 2); + vertices = &clipper.getClippedVertices(); + verticesCount = clipper.getClippedVertices().size() >> 1; + uvs = &clipper.getClippedUVs(); + indices = &clipper.getClippedTriangles(); + indicesCount = clipper.getClippedTriangles().size(); + } + + sdlVertices.clear(); + for (int ii = 0; ii < verticesCount << 1; ii += 2) { + sdlVertex.position.x = (*vertices)[ii]; + sdlVertex.position.y = (*vertices)[ii + 1]; + sdlVertex.tex_coord.x = (*uvs)[ii]; + sdlVertex.tex_coord.y = (*uvs)[ii + 1]; + sdlVertices.add(sdlVertex); + } + sdlIndices.clear(); + for (int ii = 0; ii < (int) indices->size(); ii++) + sdlIndices.add((*indices)[ii]); + + switch (slot.getData().getBlendMode()) { + case BlendMode_Normal: + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + break; + case BlendMode_Multiply: + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD); + break; + case BlendMode_Additive: + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD); + break; + case BlendMode_Screen: + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + break; + } + + SDL_RenderGeometry(renderer, texture, sdlVertices.buffer(), sdlVertices.size(), sdlIndices.buffer(), + indicesCount); + clipper.clipEnd(slot); + } + clipper.clipEnd(); +} + +SDL_Texture *loadTexture(SDL_Renderer *renderer, const String &path) { + int width, height, components; + stbi_uc *imageData = stbi_load(path.buffer(), &width, &height, &components, 4); + if (!imageData) return nullptr; + SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, + height); + if (!texture) { + stbi_image_free(imageData); + return nullptr; + } + if (SDL_UpdateTexture(texture, nullptr, imageData, width * 4)) { + stbi_image_free(imageData); + return nullptr; + } + stbi_image_free(imageData); + return texture; +} + +void SDLTextureLoader::load(AtlasPage &page, const String &path) { + SDL_Texture *texture = loadTexture(renderer, path); + if (!texture) return; + page.setRendererObject(texture); + SDL_QueryTexture(texture, nullptr, nullptr, &page.width, &page.height); + switch (page.magFilter) { + case TextureFilter_Nearest: + SDL_SetTextureScaleMode(texture, SDL_ScaleModeNearest); + break; + case TextureFilter_Linear: + SDL_SetTextureScaleMode(texture, SDL_ScaleModeLinear); + break; + default: + SDL_SetTextureScaleMode(texture, SDL_ScaleModeBest); + } +} + +void SDLTextureLoader::unload(void *texture) { + SDL_DestroyTexture((SDL_Texture *) texture); +} + +SpineExtension *spine::getDefaultExtension() { + return new DefaultSpineExtension(); +} \ No newline at end of file diff --git a/spine-sdl/src/spine-sdl-cpp.h b/spine-sdl/src/spine-sdl-cpp.h index e69de29bb..97d34bd23 100644 --- a/spine-sdl/src/spine-sdl-cpp.h +++ b/spine-sdl/src/spine-sdl-cpp.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated September 24, 2021. Replaces all prior versions. + * + * Copyright (c) 2013-2021, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, + * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef SPINE_SDL +#define SPINE_SDL + +#include + +struct SDL_Renderer; +struct SDL_Vertex; + +namespace spine { + class SkeletonDrawable { + public: + SkeletonDrawable(SkeletonData *skeletonData, AnimationStateData *animationStateData = nullptr); + + ~SkeletonDrawable(); + + void update(float delta); + + void draw(SDL_Renderer *renderer); + + Skeleton *skeleton; + AnimationState *animationState; + + private: + bool ownsAnimationStateData; + SkeletonClipping clipper; + Vector worldVertices; + Vector quadIndices; + Vector sdlVertices; + Vector sdlIndices; + }; + + class SDLTextureLoader : public spine::TextureLoader { + SDL_Renderer *renderer; + + public: + SDLTextureLoader(SDL_Renderer *renderer) : renderer(renderer) { + } + + void load(AtlasPage &page, const String &path); + + void unload(void *texture); + }; +}// namespace spine + +#endif \ No newline at end of file diff --git a/spine-sdl/src/stb_image.h b/spine-sdl/src/stb_image.h index 5ba68ce2f..c97eea70e 100644 --- a/spine-sdl/src/stb_image.h +++ b/spine-sdl/src/stb_image.h @@ -366,18 +366,17 @@ RECENT REVISION HISTORY: #ifndef STBI_NO_STDIO #include -#endif // STBI_NO_STDIO +#endif// STBI_NO_STDIO #define STBI_VERSION 1 -enum -{ - STBI_default = 0, // only used for desired_channels +enum { + STBI_default = 0,// only used for desired_channels - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 }; #include @@ -407,9 +406,9 @@ extern "C" { typedef struct { - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data + int (*read)(void *user, char *data, int size);// fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip)(void *user, int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof)(void *user); // returns nonzero if we are at end of file/data } stbi_io_callbacks; //////////////////////////////////// @@ -417,12 +416,12 @@ typedef struct // 8-bits-per-channel interface // -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); // for stbi_load_from_file, file pointer is left pointing immediately after image #endif @@ -431,7 +430,7 @@ STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int * #endif #ifdef STBI_WINDOWS_UTF8 -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t *input); #endif //////////////////////////////////// @@ -439,11 +438,11 @@ STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wch // 16-bits-per-channel interface // -STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif @@ -452,56 +451,55 @@ STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_i // float-per-channel interface // #ifndef STBI_NO_LINEAR -STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif #endif #ifndef STBI_NO_HDR -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); -STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); +STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif// STBI_NO_HDR #ifndef STBI_NO_LINEAR -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); -STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); +STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif// STBI_NO_LINEAR // stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); #ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO +STBIDEF int stbi_is_hdr(char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif// STBI_NO_STDIO // get a VERY brief reason for failure // on most compilers (and ALL modern mainstream compilers) this is threadsafe -STBIDEF const char *stbi_failure_reason (void); +STBIDEF const char *stbi_failure_reason(void); // free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); +STBIDEF void stbi_image_free(void *retval_from_stbi_load); // get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); #ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit (char const *filename); -STBIDEF int stbi_is_16_bit_from_file(FILE *f); +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit(char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); #endif - // for image formats that explicitly notate that they have premultiplied alpha, // we just return the colors as stored in the file. set this flag to force // unpremultiplication. results are undefined if the unpremultiply overflow. @@ -526,10 +524,10 @@ STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_fli STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); #ifdef __cplusplus @@ -539,41 +537,38 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch // // //// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H +#endif// STBI_INCLUDE_STB_IMAGE_H #ifdef STB_IMAGE_IMPLEMENTATION -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) || defined(STBI_ONLY_ZLIB) +#ifndef STBI_ONLY_JPEG +#define STBI_NO_JPEG +#endif +#ifndef STBI_ONLY_PNG +#define STBI_NO_PNG +#endif +#ifndef STBI_ONLY_BMP +#define STBI_NO_BMP +#endif +#ifndef STBI_ONLY_PSD +#define STBI_NO_PSD +#endif +#ifndef STBI_ONLY_TGA +#define STBI_NO_TGA +#endif +#ifndef STBI_ONLY_GIF +#define STBI_NO_GIF +#endif +#ifndef STBI_ONLY_HDR +#define STBI_NO_HDR +#endif +#ifndef STBI_ONLY_PIC +#define STBI_NO_PIC +#endif +#ifndef STBI_ONLY_PNM +#define STBI_NO_PNM +#endif #endif #if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) @@ -582,13 +577,13 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #include -#include // ptrdiff_t on osx +#include // ptrdiff_t on osx #include #include #include #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow +#include // ldexp, pow #endif #ifndef STBI_NO_STDIO @@ -608,53 +603,53 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif +#ifdef __cplusplus +#define stbi_inline inline #else - #define stbi_inline __forceinline +#define stbi_inline +#endif +#else +#define stbi_inline __forceinline #endif #ifndef STBI_NO_THREAD_LOCALS - #if defined(__cplusplus) && __cplusplus >= 201103L - #define STBI_THREAD_LOCAL thread_local - #elif defined(__GNUC__) && __GNUC__ < 5 - #define STBI_THREAD_LOCAL __thread - #elif defined(_MSC_VER) - #define STBI_THREAD_LOCAL __declspec(thread) - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) - #define STBI_THREAD_LOCAL _Thread_local - #endif +#if defined(__cplusplus) && __cplusplus >= 201103L +#define STBI_THREAD_LOCAL thread_local +#elif defined(__GNUC__) && __GNUC__ < 5 +#define STBI_THREAD_LOCAL __thread +#elif defined(_MSC_VER) +#define STBI_THREAD_LOCAL __declspec(thread) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) +#define STBI_THREAD_LOCAL _Thread_local +#endif - #ifndef STBI_THREAD_LOCAL - #if defined(__GNUC__) - #define STBI_THREAD_LOCAL __thread - #endif - #endif +#ifndef STBI_THREAD_LOCAL +#if defined(__GNUC__) +#define STBI_THREAD_LOCAL __thread +#endif +#endif #endif #ifdef _MSC_VER typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; #else #include typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; +typedef int16_t stbi__int16; typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; +typedef int32_t stbi__int32; #endif // should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; +typedef unsigned char validate_uint32[sizeof(stbi__uint32) == 4 ? 1 : -1]; #ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) +#define STBI_NOTUSED(v) (void) (v) #else -#define STBI_NOTUSED(v) (void)sizeof(v) +#define STBI_NOTUSED(v) (void) sizeof(v) #endif #ifdef _MSC_VER @@ -662,9 +657,9 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #endif #ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) +#define stbi_lrot(x, y) _lrotl(x, y) #else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) +#define stbi_lrot(x, y) (((x) << (y)) | ((x) >> (-(y) &31))) #endif #if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) @@ -676,13 +671,13 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #endif #ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p, newsz) realloc(p, newsz) +#define STBI_FREE(p) free(p) #endif #ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#define STBI_REALLOC_SIZED(p, oldsz, newsz) STBI_REALLOC(p, newsz) #endif // x86/x64 detection @@ -724,47 +719,43 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #ifdef _MSC_VER -#if _MSC_VER >= 1400 // not VC6 +#if _MSC_VER >= 1400// not VC6 #include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; +static int stbi__cpuid3(void) { + int info[4]; + __cpuid(info, 1); + return info[3]; } #else -static int stbi__cpuid3(void) -{ - int res; - __asm { +static int stbi__cpuid3(void) { + int res; + __asm { mov eax,1 cpuid mov res,edx - } - return res; + } + return res; } #endif #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name #if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; +static int stbi__sse2_available(void) { + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; } #endif -#else // assume GCC-style if not VC++ +#else// assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; +static int stbi__sse2_available(void) { + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; } #endif @@ -801,187 +792,176 @@ static int stbi__sse2_available(void) // contains all the IO context, plus some basic image information typedef struct { - stbi__uint32 img_x, img_y; - int img_n, img_out_n; + stbi__uint32 img_x, img_y; + int img_n, img_out_n; - stbi_io_callbacks io; - void *io_user_data; + stbi_io_callbacks io; + void *io_user_data; - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - int callback_already_read; + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; } stbi__context; static void stbi__refill_buffer(stbi__context *s); // initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) { + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer + len; } // initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) { + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; } #ifndef STBI_NO_STDIO -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); +static int stbi__stdio_read(void *user, char *data, int size) { + return (int) fread(data, 1, size, (FILE *) user); } -static void stbi__stdio_skip(void *user, int n) -{ - int ch; - fseek((FILE*) user, n, SEEK_CUR); - ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ - if (ch != EOF) { - ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ - } +static void stbi__stdio_skip(void *user, int n) { + int ch; + fseek((FILE *) user, n, SEEK_CUR); + ch = fgetc((FILE *) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } } -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user) || ferror((FILE *) user); +static int stbi__stdio_eof(void *user) { + return feof((FILE *) user) || ferror((FILE *) user); } static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, + { + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, }; -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +static void stbi__start_file(stbi__context *s, FILE *f) { + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); } //static void stop_file(stbi__context *s) { } -#endif // !STBI_NO_STDIO +#endif// !STBI_NO_STDIO -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; +static void stbi__rewind(stbi__context *s) { + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; } -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR +enum { + STBI_ORDER_RGB, + STBI_ORDER_BGR }; typedef struct { - int bits_per_channel; - int num_channels; - int channel_order; + int bits_per_channel; + int num_channels; + int channel_order; } stbi__result_info; #ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); #endif #ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); #endif #ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__pnm_is16(stbi__context *s); +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); #endif static #ifdef STBI_THREAD_LOCAL -STBI_THREAD_LOCAL + STBI_THREAD_LOCAL #endif -const char *stbi__g_failure_reason; + const char *stbi__g_failure_reason; -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; +STBIDEF const char *stbi_failure_reason(void) { + return stbi__g_failure_reason; } #ifndef STBI_NO_FAILURE_STRINGS -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; +static int stbi__err(const char *str) { + stbi__g_failure_reason = str; + return 0; } #endif -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); +static void *stbi__malloc(size_t size) { + return STBI_MALLOC(size); } // stb_image uses ints pervasively, including for offset calculations. @@ -996,70 +976,62 @@ static void *stbi__malloc(size_t size) // return 1 if the sum is valid, 0 on overflow. // negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; +static int stbi__addsizes_valid(int a, int b) { + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; } // returns 1 if the product is valid, 0 on overflow. // negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; +static int stbi__mul2sizes_valid(int a, int b) { + if (a < 0 || b < 0) return 0; + if (b == 0) return 1;// mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX / b; } #if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +static int stbi__mad2sizes_valid(int a, int b, int add) { + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a * b, add); } #endif // returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); +static int stbi__mad3sizes_valid(int a, int b, int c, int add) { + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a * b, c) && + stbi__addsizes_valid(a * b * c, add); } // returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a * b, c) && + stbi__mul2sizes_valid(a * b * c, d) && stbi__addsizes_valid(a * b * c * d, add); } #endif #if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); +static void *stbi__malloc_mad2(int a, int b, int add) { + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a * b + add); } #endif -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); +static void *stbi__malloc_mad3(int a, int b, int c, int add) { + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a * b * c + add); } #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a * b * c * d + add); } #endif @@ -1068,235 +1040,224 @@ static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) // stbi__errpuc - error returning pointer to unsigned char #ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 +#define stbi__err(x, y) 0 #elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) +#define stbi__err(x, y) stbi__err(y) #else - #define stbi__err(x,y) stbi__err(x) +#define stbi__err(x, y) stbi__err(x) #endif -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpf(x, y) ((float *) (size_t) (stbi__err(x, y) ? NULL : NULL)) +#define stbi__errpuc(x, y) ((unsigned char *) (size_t) (stbi__err(x, y) ? NULL : NULL)) -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); +STBIDEF void stbi_image_free(void *retval_from_stbi_load) { + STBI_FREE(retval_from_stbi_load); } #ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); #endif #ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); #endif static int stbi__vertically_flip_on_load_global = 0; -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; } #ifndef STBI_THREAD_LOCAL -#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global #else static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_local = flag_true_if_should_flip; - stbi__vertically_flip_on_load_set = 1; +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) { + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; } -#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ - ? stbi__vertically_flip_on_load_local \ - : stbi__vertically_flip_on_load_global) -#endif // STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif// STBI_THREAD_LOCAL -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB;// all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; - // test the formats with a very explicit header first (at least a FOURCC - // or distinctive magic number first) - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); - #else - STBI_NOTUSED(bpc); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); - #endif +// test the formats with a very explicit header first (at least a FOURCC +// or distinctive magic number first) +#ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s, x, y, comp, req_comp, ri); +#endif +#ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s, x, y, comp, req_comp, ri); +#endif +#ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s, x, y, comp, req_comp, ri); +#endif +#ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s, x, y, comp, req_comp, ri, bpc); +#else + STBI_NOTUSED(bpc); +#endif +#ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s, x, y, comp, req_comp, ri); +#endif - // then the formats that can end up attempting to load with just 1 or 2 - // bytes matching expectations; these are prone to false positives, so - // try them later - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); - #endif +// then the formats that can end up attempting to load with just 1 or 2 +// bytes matching expectations; these are prone to false positives, so +// try them later +#ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s, x, y, comp, req_comp, ri); +#endif +#ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s, x, y, comp, req_comp, ri); +#endif - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif +#ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x, y, comp, req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } +#endif - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); - #endif +#ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s, x, y, comp, req_comp, ri); +#endif - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) { + int i; + int img_len = w * h * channels; + stbi_uc *reduced; - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc) ((orig[i] >> 8) & 0xFF);// top half of each byte is sufficient approx of 16->8 bit scaling - STBI_FREE(orig); - return reduced; + STBI_FREE(orig); + return reduced; } -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) { + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + enlarged = (stbi__uint16 *) stbi__malloc(img_len * 2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16) ((orig[i] << 8) + orig[i]);// replicate to high and low byte, maps 0->0, 255->0xffff - STBI_FREE(orig); - return enlarged; + STBI_FREE(orig); + return enlarged; } -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) { + int row; + size_t bytes_per_row = (size_t) w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *) image; - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } + for (row = 0; row < (h >> 1); row++) { + stbi_uc *row0 = bytes + row * bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1) * bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } } #ifndef STBI_NO_GIF -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) { + int slice; + int slice_size = w * h * bytes_per_pixel; - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } + stbi_uc *bytes = (stbi_uc *) image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } } #endif -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - if (result == NULL) - return NULL; + if (result == NULL) + return NULL; - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - if (ri.bits_per_channel != 8) { - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } - // @TODO: move stbi__convert_format to here + // @TODO: move stbi__convert_format to here - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } - return (unsigned char *) result; + return (unsigned char *) result; } -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - if (result == NULL) - return NULL; + if (result == NULL) + return NULL; - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - if (ri.bits_per_channel != 16) { - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } - return (stbi__uint16 *) result; + return (stbi__uint16 *) result; } #if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } } #endif @@ -1308,258 +1269,238 @@ STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int #endif #if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) -{ +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t *input) { return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); } #endif -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; +static FILE *stbi__fopen(char const *filename, char const *mode) { + FILE *f; #if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) - wchar_t wMode[64]; - wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) - return 0; + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename) / sizeof(*wFilename))) + return 0; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) - return 0; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode) / sizeof(*wMode))) + return 0; #if defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != _wfopen_s(&f, wFilename, wMode)) f = 0; #else - f = _wfopen(wFilename, wMode); + f = _wfopen(wFilename, wMode); #endif #elif defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; + if (0 != fopen_s(&f, filename, mode)) + f = 0; #else - f = fopen(filename, mode); + f = fopen(filename, mode); #endif - return f; + return f; } -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) { + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f, x, y, comp, req_comp); + fclose(f); + return result; } -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { + unsigned char *result; + stbi__context s; + stbi__start_file(&s, f); + result = stbi__load_and_postprocess_8bit(&s, x, y, comp, req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, -(int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; } -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) { + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s, f); + result = stbi__load_and_postprocess_16bit(&s, x, y, comp, req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, -(int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; } -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) { + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f, x, y, comp, req_comp); + fclose(f); + return result; } -#endif //!STBI_NO_STDIO +#endif//!STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) { + stbi__context s; + stbi__start_mem(&s, buffer, len); + return stbi__load_and_postprocess_16bit(&s, x, y, channels_in_file, desired_channels); } -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) { + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_16bit(&s, x, y, channels_in_file, desired_channels); } -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { + stbi__context s; + stbi__start_mem(&s, buffer, len); + return stbi__load_and_postprocess_8bit(&s, x, y, comp, req_comp); } -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s, x, y, comp, req_comp); } #ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { + unsigned char *result; + stbi__context s; + stbi__start_mem(&s, buffer, len); - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } + result = (unsigned char *) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices(result, *x, *y, *z, *comp); + } - return result; + return result; } #endif #ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) { + unsigned char *data; +#ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s, x, y, comp, req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data, x, y, comp, req_comp); + return hdr_data; + } +#endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); } -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { + stbi__context s; + stbi__start_mem(&s, buffer, len); + return stbi__loadf_main(&s, x, y, comp, req_comp); } -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s, x, y, comp, req_comp); } #ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) { + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f, x, y, comp, req_comp); + fclose(f); + return result; } -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { + stbi__context s; + stbi__start_file(&s, f); + return stbi__loadf_main(&s, x, y, comp, req_comp); } -#endif // !STBI_NO_STDIO +#endif// !STBI_NO_STDIO -#endif // !STBI_NO_LINEAR +#endif// !STBI_NO_LINEAR // these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is // defined, for API simplicity; if STBI_NO_LINEAR is defined, it always // reports false! -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) { +#ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s, buffer, len); + return stbi__hdr_test(&s); +#else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; +#endif } #ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; +STBIDEF int stbi_is_hdr(char const *filename) { + FILE *f = stbi__fopen(filename, "rb"); + int result = 0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; } -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; - #else - STBI_NOTUSED(f); - return 0; - #endif +STBIDEF int stbi_is_hdr_from_file(FILE *f) { +#ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s, f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; +#else + STBI_NOTUSED(f); + return 0; +#endif } -#endif // !STBI_NO_STDIO +#endif// !STBI_NO_STDIO -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) { +#ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); +#else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; +#endif } #ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; +static float stbi__l2h_gamma = 2.2f, stbi__l2h_scale = 1.0f; -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } #endif -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; +static float stbi__h2l_gamma_i = 1.0f / 2.2f, stbi__h2l_scale_i = 1.0f; -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1 / gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1 / scale; } ////////////////////////////////////////////////////////////////////////////// @@ -1567,147 +1508,137 @@ STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; // Common code used by all image loaders // -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header +enum { + STBI__SCAN_load = 0, + STBI__SCAN_type, + STBI__SCAN_header }; -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } +static void stbi__refill_buffer(stbi__context *s) { + int n = (s->io.read)(s->io_user_data, (char *) s->buffer_start, s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + 1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } } -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; +stbi_inline static stbi_uc stbi__get8(stbi__context *s) { + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; } #if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing #else -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } +stbi_inline static int stbi__at_eof(stbi__context *s) { + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } - return s->img_buffer >= s->img_buffer_end; + return s->img_buffer >= s->img_buffer_end; } #endif #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) // nothing #else -static void stbi__skip(stbi__context *s, int n) -{ - if (n == 0) return; // already there! - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; +static void stbi__skip(stbi__context *s, int n) { + if (n == 0) return;// already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) // nothing #else -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) { + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; - memcpy(buffer, s->img_buffer, blen); + memcpy(buffer, s->img_buffer, blen); - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } + count = (s->io.read)(s->io_user_data, (char *) buffer + blen, n - blen); + res = (count == (n - blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; + if (s->img_buffer + n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; } #endif #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) // nothing #else -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); +static int stbi__get16be(stbi__context *s) { + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) // nothing #else -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); +static stbi__uint32 stbi__get32be(stbi__context *s) { + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); } #endif #if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) // nothing #else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); +static int stbi__get16le(stbi__context *s) { + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); } #endif #ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - z += (stbi__uint32)stbi__get16le(s) << 16; - return z; +static stbi__uint32 stbi__get32le(stbi__context *s) { + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32) stbi__get16le(s) << 16; + return z; } #endif -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings +#define STBI__BYTECAST(x) ((stbi_uc) ((x) &255))// truncate int to byte without warnings #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing @@ -1723,169 +1654,257 @@ static stbi__uint32 stbi__get32le(stbi__context *s) // assume data buffer is malloced, so malloc a new one and free that one // only failure mode is malloc failing -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +static stbi_uc stbi__compute_y(int r, int g, int b) { + return (stbi_uc) (((r * 77) + (g * 150) + (29 * b)) >> 8); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing #else -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { + int i, j; + unsigned char *good; - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; + for (j = 0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n; + unsigned char *dest = good + j * x * req_comp; - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } +#define STBI__COMBO(a, b) ((a) *8 + (b)) +#define STBI__CASE(a, b) \ + case STBI__COMBO(a, b): \ + for (i = x - 1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1, 2) { + dest[0] = src[0]; + dest[1] = 255; + } + break; + STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } + break; + STBI__CASE(1, 4) { + dest[0] = dest[1] = dest[2] = src[0]; + dest[3] = 255; + } + break; + STBI__CASE(2, 1) { dest[0] = src[0]; } + break; + STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } + break; + STBI__CASE(2, 4) { + dest[0] = dest[1] = dest[2] = src[0]; + dest[3] = src[1]; + } + break; + STBI__CASE(3, 4) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = 255; + } + break; + STBI__CASE(3, 1) { dest[0] = stbi__compute_y(src[0], src[1], src[2]); } + break; + STBI__CASE(3, 2) { + dest[0] = stbi__compute_y(src[0], src[1], src[2]); + dest[1] = 255; + } + break; + STBI__CASE(4, 1) { dest[0] = stbi__compute_y(src[0], src[1], src[2]); } + break; + STBI__CASE(4, 2) { + dest[0] = stbi__compute_y(src[0], src[1], src[2]); + dest[1] = src[3]; + } + break; + STBI__CASE(4, 3) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + } + break; + default: + STBI_ASSERT(0); + STBI_FREE(data); + STBI_FREE(good); + return stbi__errpuc("unsupported", "Unsupported format conversion"); + } +#undef STBI__CASE + } - STBI_FREE(data); - return good; + STBI_FREE(data); + return good; } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) // nothing #else -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) { + return (stbi__uint16) (((r * 77) + (g * 150) + (29 * b)) >> 8); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) // nothing #else -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { + int i, j; + stbi__uint16 *good; - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; + for (j = 0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n; + stbi__uint16 *dest = good + j * x * req_comp; - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } +#define STBI__COMBO(a, b) ((a) *8 + (b)) +#define STBI__CASE(a, b) \ + case STBI__COMBO(a, b): \ + for (i = x - 1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1, 2) { + dest[0] = src[0]; + dest[1] = 0xffff; + } + break; + STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } + break; + STBI__CASE(1, 4) { + dest[0] = dest[1] = dest[2] = src[0]; + dest[3] = 0xffff; + } + break; + STBI__CASE(2, 1) { dest[0] = src[0]; } + break; + STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } + break; + STBI__CASE(2, 4) { + dest[0] = dest[1] = dest[2] = src[0]; + dest[3] = src[1]; + } + break; + STBI__CASE(3, 4) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = 0xffff; + } + break; + STBI__CASE(3, 1) { dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); } + break; + STBI__CASE(3, 2) { + dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); + dest[1] = 0xffff; + } + break; + STBI__CASE(4, 1) { dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); } + break; + STBI__CASE(4, 2) { + dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); + dest[1] = src[3]; + } + break; + STBI__CASE(4, 3) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + } + break; + default: + STBI_ASSERT(0); + STBI_FREE(data); + STBI_FREE(good); + return (stbi__uint16 *) stbi__errpuc("unsupported", "Unsupported format conversion"); + } +#undef STBI__CASE + } - STBI_FREE(data); - return good; + STBI_FREE(data); + return good; } #endif #ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - } - if (n < comp) { - for (i=0; i < x*y; ++i) { - output[i*comp + n] = data[i*comp + n]/255.0f; - } - } - STBI_FREE(data); - return output; +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) { + int i, k, n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { + STBI_FREE(data); + return stbi__errpf("outofmem", "Out of memory"); + } + // compute number of non-alpha components + if (comp & 1) n = comp; + else + n = comp - 1; + for (i = 0; i < x * y; ++i) { + for (k = 0; k < n; ++k) { + output[i * comp + k] = (float) (pow(data[i * comp + k] / 255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i = 0; i < x * y; ++i) { + output[i * comp + n] = data[i * comp + n] / 255.0f; + } + } + STBI_FREE(data); + return output; } #endif #ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) { + int i, k, n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + // compute number of non-alpha components + if (comp & 1) n = comp; + else + n = comp - 1; + for (i = 0; i < x * y; ++i) { + for (k = 0; k < n; ++k) { + float z = (float) pow(data[i * comp + k] * stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i * comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i * comp + k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i * comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; } #endif @@ -1913,749 +1932,744 @@ static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) #ifndef STBI_NO_JPEG // huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache +#define FAST_BITS 9// larger handles more cases; smaller stomps less cache typedef struct { - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17];// old 'firstsymbol' - old 'firstcode' } stbi__huffman; typedef struct { - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; + // sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; + // definition of jpeg image component + struct + { + int id; + int h, v; + int tq; + int hd, ha; + int dc_pred; - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; + int x, y, w2, h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h;// number of 8x8 coefficient blocks + } img_comp[4]; - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop + stbi__uint32 code_buffer;// jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform;// Adobe APP14 tag + int rgb; - int scan_n, order[4]; - int restart_interval, todo; + int scan_n, order[4]; + int restart_interval, todo; -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); + // kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); } stbi__jpeg; -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; +static int stbi__build_huffman(stbi__huffman *h, int *count) { + int i, j, k = 0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i = 0; i < 16; ++i) + for (j = 0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i + 1); + h->size[k] = 0; - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for (j = 1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code - 1 >= (1u << j)) return stbi__err("bad code lengths", "Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16 - j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i = 0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS - s); + int m = 1 << (FAST_BITS - s); + for (j = 0; j < m; ++j) { + h->fast[c + j] = (stbi_uc) i; + } + } + } + return 1; } // build a table that decodes both magnitude and value of small ACs in // one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) { + int i; + for (i = 0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } - } + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } } -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) { + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s);// consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); } // (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; +static const stbi__uint32 stbi__bmask[17] = {0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535}; // decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) { + unsigned int temp; + int c, k; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k = FAST_BITS + 1;; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } - if (k > j->code_bits) - return -1; + if (k > j->code_bits) + return -1; - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; } // bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); +stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) { + unsigned int k; + int sgn; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & (sgn - 1)); + sgn = j->code_buffer >> 31;// sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); } // get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; } -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) { + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; } // given a value that's at position X in the zigzag stream, // where does it appear in the 8x8 matrix coded as row-major? -static const stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; +static const stbi_uc stbi__jpeg_dezigzag[64 + 15] = + { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63}; // decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff,dc,k; - int t; +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) { + int diff, dc, k; + int t; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("bad huffman code", "Corrupt JPEG"); - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); + // 0 all the ac values now so we can do it 32-bits at a time + memset(data, 0, 64 * sizeof(data[0])); - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c, r, s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15;// run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code", "Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break;// end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j, s) * dequant[zig]); + } + } + } while (k < 64); + return 1; } -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) { + int diff, dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - diff = t ? stbi__extend_receive(j, t) : 0; + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data, 0, 64 * sizeof(data[0]));// 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * (1 << j->succ_low)); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; } // @OPTIMIZE: store non-zigzagged during the decode passes, // and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) { + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - if (j->succ_high == 0) { - int shift = j->succ_low; + if (j->succ_high == 0) { + int shift = j->succ_low; - if (j->eob_run) { - --j->eob_run; - return 1; - } + if (j->eob_run) { + --j->eob_run; + return 1; + } - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * (1 << shift)); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients + k = j->spec_start; + do { + unsigned int zig; + int c, r, s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15;// run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code", "Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j, s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients - short bit = (short) (1 << j->succ_low); + short bit = (short) (1 << j->succ_low); - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit) == 0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r, s; + int rs = stbi__jpeg_huff_decode(j, hac);// @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code", "Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64;// force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit) == 0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; } // take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; +stbi_inline static stbi_uc stbi__clamp(int x) { + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; } -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) * 4096) +#define stbi__f2f(x) ((int) (((x) *4096 + 0.5))) +#define stbi__fsh(x) ((x) *4096) // derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; +#define STBI__IDCT_1D(s0, s1, s2, s3, s4, s5, s6, s7) \ + int t0, t1, t2, t3, p1, p2, p3, p4, p5, x0, x1, x2, x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2 + p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3 * stbi__f2f(-1.847759065f); \ + t3 = p1 + p2 * stbi__f2f(0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2 + p3); \ + t1 = stbi__fsh(p2 - p3); \ + x0 = t0 + t3; \ + x3 = t0 - t3; \ + x1 = t1 + t2; \ + x2 = t1 - t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0 + t2; \ + p4 = t1 + t3; \ + p1 = t0 + t3; \ + p2 = t1 + t2; \ + p5 = (p3 + p4) * stbi__f2f(1.175875602f); \ + t0 = t0 * stbi__f2f(0.298631336f); \ + t1 = t1 * stbi__f2f(2.053119869f); \ + t2 = t2 * stbi__f2f(3.072711026f); \ + t3 = t3 * stbi__f2f(1.501321110f); \ + p1 = p5 + p1 * stbi__f2f(-0.899976223f); \ + p2 = p5 + p2 * stbi__f2f(-2.562915447f); \ + p3 = p3 * stbi__f2f(-1.961570560f); \ + p4 = p4 * stbi__f2f(-0.390180644f); \ + t3 += p1 + p4; \ + t2 += p2 + p3; \ + t1 += p2 + p4; \ + t0 += p1 + p3; -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) { + int i, val[64], *v = val; + stbi_uc *o; + short *d = data; - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } + // columns + for (i = 0; i < 8; ++i, ++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[8] == 0 && d[16] == 0 && d[24] == 0 && d[32] == 0 && d[40] == 0 && d[48] == 0 && d[56] == 0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * 4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[0], d[8], d[16], d[24], d[32], d[40], d[48], d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; + x1 += 512; + x2 += 512; + x3 += 512; + v[0] = (x0 + t3) >> 10; + v[56] = (x0 - t3) >> 10; + v[8] = (x1 + t2) >> 10; + v[48] = (x1 - t2) >> 10; + v[16] = (x2 + t1) >> 10; + v[40] = (x2 - t1) >> 10; + v[24] = (x3 + t0) >> 10; + v[32] = (x3 - t0) >> 10; + } + } - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } + for (i = 0, v = val, o = out; i < 8; ++i, v += 8, o += out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128 << 17); + x1 += 65536 + (128 << 17); + x2 += 65536 + (128 << 17); + x3 += 65536 + (128 << 17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0 + t3) >> 17); + o[7] = stbi__clamp((x0 - t3) >> 17); + o[1] = stbi__clamp((x1 + t2) >> 17); + o[6] = stbi__clamp((x1 - t2) >> 17); + o[2] = stbi__clamp((x2 + t1) >> 17); + o[5] = stbi__clamp((x2 - t1) >> 17); + o[3] = stbi__clamp((x3 + t0) >> 17); + o[4] = stbi__clamp((x3 - t0) >> 17); + } } #ifdef STBI_SSE2 // sse2 integer IDCT. not the fastest possible implementation but it // produces bit-identical results to the generic C version so it's // fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) +// dot product constant: even elems=x, odd elems=y +#define dct_const(x, y) _mm_setr_epi16((x), (y), (x), (y), (x), (y), (x), (y)) - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) +// out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) +// out(1) = c1[even]*x + c1[odd]*y +#define dct_rot(out0, out1, x, y, c0, c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x), (y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x), (y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) +// out = in << 12 (in 16-bit, out 32-bit) +#define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) +// wide add +#define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) +// wide sub +#define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } +// butterfly a/b, add bias, then shift by "s" and pack +#define dct_bfly32o(out0, out1, a, b, bias, s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) +// 8-bit interleave step (for transposes) +#define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) +// 16-bit interleave step (for transposes) +#define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } +#define dct_pass(bias, shift) \ + { \ + /* even part */ \ + dct_rot(t2e, t3e, row2, row6, rot0_0, rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o, y2o, row7, row3, rot2_0, rot2_1); \ + dct_rot(y1o, y3o, row5, row1, rot3_0, rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o, y5o, sum17, sum35, rot1_0, rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0, row7, x0, x7, bias, shift); \ + dct_bfly32o(row1, row6, x1, x6, bias, shift); \ + dct_bfly32o(row2, row5, x2, x5, bias, shift); \ + dct_bfly32o(row3, row4, x3, x4, bias, shift); \ + } - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f(0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f(0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f(3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f(2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f(1.501321110f)); - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128 << 17)); - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + // load + row0 = _mm_load_si128((const __m128i *) (data + 0 * 8)); + row1 = _mm_load_si128((const __m128i *) (data + 1 * 8)); + row2 = _mm_load_si128((const __m128i *) (data + 2 * 8)); + row3 = _mm_load_si128((const __m128i *) (data + 3 * 8)); + row4 = _mm_load_si128((const __m128i *) (data + 4 * 8)); + row5 = _mm_load_si128((const __m128i *) (data + 5 * 8)); + row6 = _mm_load_si128((const __m128i *) (data + 6 * 8)); + row7 = _mm_load_si128((const __m128i *) (data + 7 * 8)); - // column pass - dct_pass(bias_0, 10); + // column pass + dct_pass(bias_0, 10); - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } - // row pass - dct_pass(bias_1, 17); + // row pass + dct_pass(bias_1, 17); - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1);// a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2);// a0e0a1e1... + dct_interleave8(p1, p3);// c0g0c1g1... - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... + // transpose pass 2 + dct_interleave8(p0, p1);// a0c0e0g0... + dct_interleave8(p2, p3);// b0d0f0h0... - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... + // transpose pass 3 + dct_interleave8(p0, p2);// a0b0c0d0... + dct_interleave8(p1, p3);// a4b4c4d4... - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } + // store + _mm_storel_epi64((__m128i *) out, p0); + out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); + out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); + out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); + out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); + out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); + out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); + out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } #undef dct_const #undef dct_rot @@ -2668,204 +2682,241 @@ static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) #undef dct_pass } -#endif // STBI_SSE2 +#endif// STBI_SSE2 #ifdef STBI_NEON // NEON integer IDCT. should produce bit-identical // results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f(0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f(1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f(0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f(2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f(3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f(1.501321110f)); -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) // wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) // wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) // butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } +#define dct_bfly32o(out0, out1, a, b, shiftop, s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0, row7, x0, x7, shiftop, shift); \ + dct_bfly32o(row1, row6, x1, x6, shiftop, shift); \ + dct_bfly32o(row2, row5, x2, x5, shiftop, shift); \ + dct_bfly32o(row3, row4, x3, x4, shiftop, shift); \ + } - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); + // load + row0 = vld1q_s16(data + 0 * 8); + row1 = vld1q_s16(data + 1 * 8); + row2 = vld1q_s16(data + 2 * 8); + row3 = vld1q_s16(data + 3 * 8); + row4 = vld1q_s16(data + 4 * 8); + row5 = vld1q_s16(data + 5 * 8); + row6 = vld1q_s16(data + 6 * 8); + row7 = vld1q_s16(data + 7 * 8); - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - // column pass - dct_pass(vrshrn_n_s32, 10); + // column pass + dct_pass(vrshrn_n_s32, 10); - // 16bit 8x8 transpose - { + // 16bit 8x8 transpose + { // these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. // whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } +#define dct_trn16(x, y) \ + { \ + int16x8x2_t t = vtrnq_s16(x, y); \ + x = t.val[0]; \ + y = t.val[1]; \ + } +#define dct_trn32(x, y) \ + { \ + int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); \ + x = vreinterpretq_s16_s32(t.val[0]); \ + y = vreinterpretq_s16_s32(t.val[1]); \ + } +#define dct_trn64(x, y) \ + { \ + int16x8_t x0 = x; \ + int16x8_t y0 = y; \ + x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); \ + y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); \ + } - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); + // pass 1 + dct_trn16(row0, row1);// a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); + // pass 2 + dct_trn32(row0, row2);// a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); + // pass 3 + dct_trn64(row0, row4);// a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); #undef dct_trn16 #undef dct_trn32 #undef dct_trn64 - } + } - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) \ + { \ + uint8x8x2_t t = vtrn_u8(x, y); \ + x = t.val[0]; \ + y = t.val[1]; \ + } +#define dct_trn8_16(x, y) \ + { \ + uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); \ + x = vreinterpret_u8_u16(t.val[0]); \ + y = vreinterpret_u8_u16(t.val[1]); \ + } +#define dct_trn8_32(x, y) \ + { \ + uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); \ + x = vreinterpret_u8_u32(t.val[0]); \ + y = vreinterpret_u8_u32(t.val[1]); \ + } - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); + // store + vst1_u8(out, p0); + out += out_stride; + vst1_u8(out, p1); + out += out_stride; + vst1_u8(out, p2); + out += out_stride; + vst1_u8(out, p3); + out += out_stride; + vst1_u8(out, p4); + out += out_stride; + vst1_u8(out, p5); + out += out_stride; + vst1_u8(out, p6); + out += out_stride; + vst1_u8(out, p7); #undef dct_trn8_8 #undef dct_trn8_16 #undef dct_trn8_32 - } + } #undef dct_long_mul #undef dct_long_mac @@ -2876,1148 +2927,1178 @@ static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) #undef dct_pass } -#endif // STBI_NEON +#endif// STBI_NEON -#define STBI__MARKER_none 0xff +#define STBI__MARKER_none 0xff // if there's a pending marker from the entropy stream, return that // otherwise, fetch from the stream and get a marker. if there's no // marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; +static stbi_uc stbi__get_marker(stbi__jpeg *j) { + stbi_uc x; + if (j->marker != STBI__MARKER_none) { + x = j->marker; + j->marker = STBI__MARKER_none; + return x; + } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s);// consume repeated 0xff fill bytes + return x; } // in each scan, we'll have scan_n components, and the order // of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) // after a restart interval, stbi__jpeg_reset the entropy decoder and // the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels +static void stbi__jpeg_reset(stbi__jpeg *j) { + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels } -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) { + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i, j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x + 7) >> 3; + int h = (z->img_comp[n].y + 7) >> 3; + for (j = 0; j < h; ++j) { + for (i = 0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc + z->img_comp[n].hd, z->huff_ac + ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * j * 8 + i * 8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else {// interleaved + int i, j, k, x, y; + STBI_SIMD_ALIGN(short, data[64]); + for (j = 0; j < z->img_mcu_y; ++j) { + for (i = 0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k = 0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y = 0; y < z->img_comp[n].v; ++y) { + for (x = 0; x < z->img_comp[n].h; ++x) { + int x2 = (i * z->img_comp[n].h + x) * 8; + int y2 = (j * z->img_comp[n].v + y) * 8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc + z->img_comp[n].hd, z->huff_ac + ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * y2 + x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i, j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x + 7) >> 3; + int h = (z->img_comp[n].y + 7) >> 3; + for (j = 0; j < h; ++j) { + for (i = 0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else {// interleaved + int i, j, k, x, y; + for (j = 0; j < z->img_mcu_y; ++j) { + for (i = 0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k = 0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y = 0; y < z->img_comp[n].v; ++y) { + for (x = 0; x < z->img_comp[n].h; ++x) { + int x2 = (i * z->img_comp[n].h + x); + int y2 = (j * z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } } -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) { + int i; + for (i = 0; i < 64; ++i) + data[i] *= dequant[i]; } -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } +static void stbi__jpeg_finish(stbi__jpeg *z) { + if (z->progressive) { + // dequantize and idct the data + int i, j, n; + for (n = 0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x + 7) >> 3; + int h = (z->img_comp[n].y + 7) >> 3; + for (j = 0; j < h; ++j) { + for (i = 0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * j * 8 + i * 8, z->img_comp[n].w2, data); + } + } + } + } } -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); +static int stbi__process_marker(stbi__jpeg *z, int m) { + int L; + switch (m) { + case STBI__MARKER_none:// no marker found + return stbi__err("expected marker", "Corrupt JPEG"); - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; + case 0xDD:// DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len", "Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + case 0xDB:// DQT - define quantization table + L = stbi__get16be(z->s) - 2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15, i; + if (p != 0 && p != 1) return stbi__err("bad DQT type", "Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table", "Corrupt JPEG"); - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; + for (i = 0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16) (sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L == 0; - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } + case 0xC4:// DHT - define huffman table + L = stbi__get16be(z->s) - 2; + while (L > 0) { + stbi_uc *v; + int sizes[16], i, n = 0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header", "Corrupt JPEG"); + for (i = 0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc + th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac + th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i = 0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L == 0; + } - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len", "Corrupt JPEG"); + else + return stbi__err("bad APP len", "Corrupt JPEG"); + } + L -= 2; - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } + if (m == 0xE0 && L >= 5) {// JFIF APP0 segment + static const unsigned char tag[5] = {'J', 'F', 'I', 'F', '\0'}; + int ok = 1; + int i; + for (i = 0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) {// Adobe APP14 segment + static const unsigned char tag[6] = {'A', 'd', 'o', 'b', 'e', '\0'}; + int ok = 1; + int i; + for (i = 0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s);// color transform + L -= 6; + } + } - stbi__skip(z->s, L); - return 1; - } + stbi__skip(z->s, L); + return 1; + } - return stbi__err("unknown marker","Corrupt JPEG"); + return stbi__err("unknown marker", "Corrupt JPEG"); } // after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } +static int stbi__process_scan_header(stbi__jpeg *z) { + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count", "Corrupt JPEG"); + if (Ls != 6 + 2 * z->scan_n) return stbi__err("bad SOS len", "Corrupt JPEG"); + for (i = 0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0;// no match + z->img_comp[which].hd = q >> 4; + if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff", "Corrupt JPEG"); + z->img_comp[which].ha = q & 15; + if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff", "Corrupt JPEG"); + z->order[i] = which; + } - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s);// should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS", "Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS", "Corrupt JPEG"); + z->spec_end = 63; + } + } - return 1; + return 1; } -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) { + int i; + for (i = 0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; } -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } +static int stbi__process_frame_header(stbi__jpeg *z, int scan) { + stbi__context *s = z->s; + int Lf, p, i, q, h_max = 1, v_max = 1, c; + Lf = stbi__get16be(s); + if (Lf < 11) return stbi__err("bad SOF len", "Corrupt JPEG");// JPEG + p = stbi__get8(s); + if (p != 8) return stbi__err("only 8-bit", "JPEG format not supported: 8-bit only");// JPEG baseline + s->img_y = stbi__get16be(s); + if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height");// Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); + if (s->img_x == 0) return stbi__err("0 width", "Corrupt JPEG");// JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large", "Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large", "Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count", "Corrupt JPEG"); + s->img_n = c; + for (i = 0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + if (Lf != 8 + 3 * s->img_n) return stbi__err("bad SOF len", "Corrupt JPEG"); - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } + z->rgb = 0; + for (i = 0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = {'R', 'G', 'B'}; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); + if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H", "Corrupt JPEG"); + z->img_comp[i].v = q & 15; + if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V", "Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); + if (z->img_comp[i].tq > 3) return stbi__err("bad TQ", "Corrupt JPEG"); + } - if (scan != STBI__SCAN_load) return 1; + if (scan != STBI__SCAN_load) return 1; - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } + for (i = 0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } - // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios - // and I've never seen a non-corrupted JPEG file actually use them - for (i=0; i < s->img_n; ++i) { - if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); - if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); - } + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i = 0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H", "Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V", "Corrupt JPEG"); + } - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w - 1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h - 1) / z->img_mcu_h; - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } - } + for (i = 0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max - 1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max - 1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i + 1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc *) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i + 1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short *) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } - return 1; + return 1; } // use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) -#define stbi__SOF_progressive(x) ((x) == 0xc2) +#define stbi__SOF_progressive(x) ((x) == 0xc2) -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) { + int m; + z->jfif = 0; + z->app14_color_transform = -1;// valid values are 0,1,2 + z->marker = STBI__MARKER_none;// initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI", "Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z, m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; } // decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; +static int stbi__decode_jpeg_image(stbi__jpeg *j) { + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; } // static jfif-centered resampling (across block boundaries) typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); + int w, int hs); #define stbi__div4(x) ((stbi_uc) ((x) >> 2)) -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; } -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; +static stbi_uc *stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i = 0; i < w; ++i) + out[i] = stbi__div4(3 * in_near[i] + in_far[i] + 2); + return out; } -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; +static stbi_uc *stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; + out[0] = input[0]; + out[1] = stbi__div4(input[0] * 3 + input[1] + 2); + for (i = 1; i < w - 1; ++i) { + int n = 3 * input[i] + 2; + out[i * 2 + 0] = stbi__div4(n + input[i - 1]); + out[i * 2 + 1] = stbi__div4(n + input[i + 1]); + } + out[i * 2 + 0] = stbi__div4(input[w - 2] * 3 + input[w - 1] + 2); + out[i * 2 + 1] = input[w - 1]; - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); - return out; + return out; } #define stbi__div16(x) ((stbi_uc) ((x) >> 4)) -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { + // need to generate 2x2 samples for every one in input + int i, t0, t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3 * in_near[0] + in_far[0] + 2); + return out; + } - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); + t1 = 3 * in_near[0] + in_far[0]; + out[0] = stbi__div4(t1 + 2); + for (i = 1; i < w; ++i) { + t0 = t1; + t1 = 3 * in_near[i] + in_far[i]; + out[i * 2 - 1] = stbi__div16(3 * t0 + t1 + 8); + out[i * 2] = stbi__div16(3 * t1 + t0 + 8); + } + out[w * 2 - 1] = stbi__div4(t1 + 2); - STBI_NOTUSED(hs); + STBI_NOTUSED(hs); - return out; + return out; } #if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { + // need to generate 2x2 samples for every one in input + int i = 0, t0, t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } + if (w == 1) { + out[0] = out[1] = stbi__div4(3 * in_near[0] + in_far[0] + 2); + return out; + } - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { + t1 = 3 * in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w - 1) & ~7); i += 8) { #if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff);// current row - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3 * in_near[i + 8] + in_far[i + 8], 7); - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i * 2), outv); #elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff);// current row - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3 * in_near[i + 8] + in_far[i + 8], nxt0, 7); - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i * 2, o); #endif - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } + // "previous" value for next iter + t1 = 3 * in_near[i + 7] + in_far[i + 7]; + } - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); + t0 = t1; + t1 = 3 * in_near[i] + in_far[i]; + out[i * 2] = stbi__div16(3 * t1 + t0 + 8); - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3 * in_near[i] + in_far[i]; + out[i * 2 - 1] = stbi__div16(3 * t0 + t1 + 8); + out[i * 2] = stbi__div16(3 * t1 + t0 + 8); + } + out[w * 2 - 1] = stbi__div4(t1 + 2); - STBI_NOTUSED(hs); + STBI_NOTUSED(hs); - return out; + return out; } #endif -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { + // resample with nearest-neighbor + int i, j; + STBI_NOTUSED(in_far); + for (i = 0; i < w; ++i) + for (j = 0; j < hs; ++j) + out[i * hs + j] = in_near[i]; + return out; } // this is a reduced-precision calculation of YCbCr-to-RGB introduced // to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } +#define stbi__float2fixed(x) (((int) ((x) *4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) { + int i; + for (i = 0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1 << 19);// rounding + int r, g, b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr * stbi__float2fixed(1.40200f); + g = y_fixed + (cr * -stbi__float2fixed(0.71414f)) + ((cb * -stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb * stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { + if (r < 0) r = 0; + else + r = 255; + } + if ((unsigned) g > 255) { + if (g < 0) g = 0; + else + g = 255; + } + if ((unsigned) b > 255) { + if (b < 0) b = 0; + else + b = 255; + } + out[0] = (stbi_uc) r; + out[1] = (stbi_uc) g; + out[2] = (stbi_uc) b; + out[3] = 255; + out += step; + } } #if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) { + int i = 0; #ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16((short) (1.40200f * 4096.0f + 0.5f)); + __m128i cr_const1 = _mm_set1_epi16(-(short) (0.71414f * 4096.0f + 0.5f)); + __m128i cb_const0 = _mm_set1_epi16(-(short) (0.34414f * 4096.0f + 0.5f)); + __m128i cb_const1 = _mm_set1_epi16((short) (1.77200f * 4096.0f + 0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255);// alpha channel - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + for (; i + 7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y + i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr + i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb + i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip);// -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip);// -128 - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } #endif #ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16((short) (1.40200f * 4096.0f + 0.5f)); + int16x8_t cr_const1 = vdupq_n_s16(-(short) (0.71414f * 4096.0f + 0.5f)); + int16x8_t cb_const0 = vdupq_n_s16(-(short) (0.34414f * 4096.0f + 0.5f)); + int16x8_t cb_const1 = vdupq_n_s16((short) (1.77200f * 4096.0f + 0.5f)); - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + for (; i + 7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8 * 4; + } + } #endif - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1 << 19);// rounding + int r, g, b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr * stbi__float2fixed(1.40200f); + g = y_fixed + cr * -stbi__float2fixed(0.71414f) + ((cb * -stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb * stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { + if (r < 0) r = 0; + else + r = 255; + } + if ((unsigned) g > 255) { + if (g < 0) g = 0; + else + g = 255; + } + if ((unsigned) b > 255) { + if (b < 0) b = 0; + else + b = 255; + } + out[0] = (stbi_uc) r; + out[1] = (stbi_uc) g; + out[2] = (stbi_uc) b; + out[3] = 255; + out += step; + } } #endif // set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; +static void stbi__setup_jpeg(stbi__jpeg *j) { + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; #ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } #endif #ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; #endif } // clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); +static void stbi__cleanup_jpeg(stbi__jpeg *j) { + stbi__free_jpeg_components(j, j->s->img_n, 0); } typedef struct { - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on + resample_row_func resample; + stbi_uc *line0, *line1; + int hs, vs; // expansion factor in each axis + int w_lores;// horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on } stbi__resample; // fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) { + unsigned int t = x * y + 128; + return (stbi_uc) ((t + (t >> 8)) >> 8); } -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) { + int n, decode_n, is_rgb; + z->s->img_n = 0;// make stbi__cleanup_jpeg safe - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { + stbi__cleanup_jpeg(z); + return NULL; + } - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 + : 1; - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; - // nothing to do if no components requested; check this now to avoid - // accessing uninitialized coutput[0] later - if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { + stbi__cleanup_jpeg(z); + return NULL; + } - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + // resample and color-convert + { + int k; + unsigned int i, j; + stbi_uc *output; + stbi_uc *coutput[4] = {NULL, NULL, NULL, NULL}; - stbi__resample res_comp[4]; + stbi__resample res_comp[4]; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; + for (k = 0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { + stbi__cleanup_jpeg(z); + return stbi__errpuc("outofmem", "Out of memory"); + } - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs - 1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) + r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) + r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) + r->resample = z->resample_row_hv_2_kernel; + else + r->resample = stbi__resample_row_generic; + } - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { + stbi__cleanup_jpeg(z); + return stbi__errpuc("outofmem", "Out of memory"); + } - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } + // now go ahead and resample + for (j = 0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k = 0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i = 0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) {// CMYK + for (i = 0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) {// YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i = 0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else {// YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i = 0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255;// not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i = 0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i = 0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i = 0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i = 0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i = 0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i = 0; i < z->s->img_x; ++i) { + *out++ = y[i]; + *out++ = 255; + } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1;// report original components, not output + return output; + } } -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - if (!j) return stbi__errpuc("outofmem", "Out of memory"); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + unsigned char *result; + stbi__jpeg *j = (stbi__jpeg *) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x, y, comp, req_comp); + STBI_FREE(j); + return result; } -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - if (!j) return stbi__err("outofmem", "Out of memory"); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; +static int stbi__jpeg_test(stbi__context *s) { + int r; + stbi__jpeg *j = (stbi__jpeg *) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; } -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) { + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind(j->s); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; } -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - if (!j) return stbi__err("outofmem", "Out of memory"); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { + int result; + stbi__jpeg *j = (stbi__jpeg *) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; } #endif @@ -4031,84 +4112,81 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_ZLIB // fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) -#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet +#define STBI__ZFAST_BITS 9// accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288// number of symbols in literal/length alphabet // zlib-style huffman encoding // (jpegs packs from left, zlib from right, so can't share code) typedef struct { - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[STBI__ZNSYMS]; - stbi__uint16 value[STBI__ZNSYMS]; + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; } stbi__zhuffman; -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; +stbi_inline static int stbi__bitreverse16(int n) { + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; } -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); +stbi_inline static int stbi__bit_reverse(int v, int bits) { + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16 - bits); } -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) { + int i, k = 0; + int code, next_code[16], sizes[17]; - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i = 0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i = 1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i = 1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code - 1 >= (1 << i)) return stbi__err("bad codelengths", "Corrupt PNG"); + z->maxcode[i] = code << (16 - i);// preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000;// sentinel + for (i = 0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size[c] = (stbi_uc) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s], s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; } // zlib-from-memory implementation for PNG reading @@ -4119,275 +4197,269 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int typedef struct { - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; - stbi__zhuffman z_length, z_distance; + stbi__zhuffman z_length, z_distance; } stbi__zbuf; -stbi_inline static int stbi__zeof(stbi__zbuf *z) -{ - return (z->zbuffer >= z->zbuffer_end); +stbi_inline static int stbi__zeof(stbi__zbuf *z) { + return (z->zbuffer >= z->zbuffer_end); } -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - return stbi__zeof(z) ? 0 : *z->zbuffer++; +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) { + return stbi__zeof(z) ? 0 : *z->zbuffer++; } -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - if (z->code_buffer >= (1U << z->num_bits)) { - z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ - return; - } - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); +static void stbi__fill_bits(stbi__zbuf *z) { + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); } -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) { + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; } -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s >= 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! - if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) { + int b, s, k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s = STBI__ZFAST_BITS + 1;; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1;// invalid code! + // code size is s, so: + b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1;// some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; } -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) { - if (stbi__zeof(a)) { - return -1; /* report error for unexpected end of data. */ - } - stbi__fill_bits(a); - } - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { + int b, s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); } -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n)// need to make room for n bytes { - char *q; - unsigned int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (unsigned int) (z->zout - z->zout_start); - limit = old_limit = (unsigned) (z->zout_end - z->zout_start); - if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); - while (cur + n > limit) { - if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); - limit *= 2; - } - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit", "Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if (limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; } static const int stbi__zlength_base[31] = { - 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,0,0 }; + 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, 0, 0}; -static const int stbi__zlength_extra[31]= -{ 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,0,0 }; +static const int stbi__zlength_extra[31] = + {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, 0, 0}; -static const int stbi__zdist_base[32] = { 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,0,0}; +static const int stbi__zdist_base[32] = {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, 0, 0}; static const int stbi__zdist_extra[32] = -{ 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}; + {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}; -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } +static int stbi__parse_huffman_block(stbi__zbuf *a) { + char *zout = a->zout; + for (;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code", "Corrupt PNG");// error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len, dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code", "Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist", "Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) {// run of one byte; common in images. + stbi_uc v = *p; + if (len) { + do *zout++ = v; + while (--len); + } + } else { + if (len) { + do *zout++ = *p++; + while (--len); + } + } + } + } } -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; +static int stbi__compute_huffman_codes(stbi__zbuf *a) { + static const stbi_uc length_dezigzag[19] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286 + 32 + 137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i, n; - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; + int hlit = stbi__zreceive(a, 5) + 257; + int hdist = stbi__zreceive(a, 5) + 1; + int hclen = stbi__zreceive(a, 4) + 4; + int ntot = hlit + hdist; - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i = 0; i < hclen; ++i) { + int s = stbi__zreceive(a, 3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - n = 0; - while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - } else if (c == 18) { - c = stbi__zreceive(a,7)+11; - } else { - return stbi__err("bad codelengths", "Corrupt PNG"); - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } - } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a, 2) + 3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n - 1]; + } else if (c == 17) { + c = stbi__zreceive(a, 3) + 3; + } else if (c == 18) { + c = stbi__zreceive(a, 7) + 11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes + n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths", "Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes + hlit, hdist)) return 0; + return 1; } -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; +static int stbi__parse_uncompressed_block(stbi__zbuf *a) { + stbi_uc header[4]; + int len, nlen, k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7);// discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255);// suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt", "Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt", "Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer", "Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; } -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; +static int stbi__parse_zlib_header(stbi__zbuf *a) { + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header", "Corrupt PNG"); // zlib spec + if ((cmf * 256 + flg) % 31 != 0) return stbi__err("bad zlib header", "Corrupt PNG");// zlib spec + if (flg & 32) return stbi__err("no preset dict", "Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression", "Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; } static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8}; static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; + { + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}; /* Init algorithm: { @@ -4401,117 +4473,109 @@ Init algorithm: } */ -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) { + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a, 1); + type = stbi__zreceive(a, 2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length, stbi__zdefault_length, STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; } -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) { + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; - return stbi__parse_zlib(a, parse_header); + return stbi__parse_zlib(a, parse_header); } -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) { + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } } -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) { + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); } -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) { + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } } -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) { + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; } -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) { + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } } -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) { + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; } #endif @@ -4528,1129 +4592,1196 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char #ifndef STBI_NO_PNG typedef struct { - stbi__uint32 length; - stbi__uint32 type; + stbi__uint32 length; + stbi__uint32 type; } stbi__pngchunk; -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) { + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; } -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; +static int stbi__check_png_header(stbi__context *s) { + static const stbi_uc png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + int i; + for (i = 0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig", "Not a PNG"); + return 1; } typedef struct { - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; } stbi__png; enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first + STBI__F_none = 0, + STBI__F_sub = 1, + STBI__F_up = 2, + STBI__F_avg = 3, + STBI__F_paeth = 4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first }; static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; + { + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first}; -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; +static int stbi__paeth(int a, int b, int c) { + int p = a + b - c; + int pa = abs(p - a); + int pb = abs(p - b); + int pc = abs(p - c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; } -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; +static const stbi_uc stbi__depth_scale_table[9] = {0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01}; // create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) { + int bytes = (depth == 16 ? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i, j, stride = x * out_n * bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n;// copy it into a local for later - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; + int output_bytes = out_n * bytes; + int filter_bytes = img_n * bytes; + int width = x; - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n + 1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0);// extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels", "Corrupt PNG"); - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; - int filter = *raw++; + for (j = 0; j < y; ++j) { + stbi_uc *cur = a->out + stride * j; + stbi_uc *prior; + int filter = *raw++; - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); + if (filter > 4) + return stbi__err("invalid filter", "Corrupt PNG"); - if (depth < 8) { - if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width", "Corrupt PNG"); + cur += x * out_n - img_width_bytes;// store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride;// bugfix: need to compute this after 'cur +=' computation above - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } + // handle first byte explicitly + for (k = 0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none: + cur[k] = raw[k]; + break; + case STBI__F_sub: + cur[k] = raw[k]; + break; + case STBI__F_up: + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); + break; + case STBI__F_avg: + cur[k] = STBI__BYTECAST(raw[k] + (prior[k] >> 1)); + break; + case STBI__F_paeth: + cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0, prior[k], 0)); + break; + case STBI__F_avg_first: + cur[k] = raw[k]; + break; + case STBI__F_paeth_first: + cur[k] = raw[k]; + break; + } + } - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255;// first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes + 1] = 255;// first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1) * filter_bytes; +#define STBI__CASE(f) \ + case f: \ + for (k = 0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: + memcpy(cur, raw, nk); + break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k - filter_bytes]); } + break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } + break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1)); } + break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k - filter_bytes], prior[k], prior[k - filter_bytes])); } + break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k - filter_bytes] >> 1)); } + break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k - filter_bytes], 0, 0)); } + break; + } +#undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n + 1 == out_n); +#define STBI__CASE(f) \ + case f: \ + for (i = x - 1; i >= 1; --i, cur[filter_bytes] = 255, raw += filter_bytes, cur += output_bytes, prior += output_bytes) \ + for (k = 0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } + break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k - output_bytes]); } + break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } + break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - output_bytes]) >> 1)); } + break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k - output_bytes], prior[k], prior[k - output_bytes])); } + break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k - output_bytes] >> 1)); } + break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k - output_bytes], 0, 0)); } + break; + } +#undef STBI__CASE - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride * j;// start at the beginning of the row again + for (i = 0; i < x; ++i, cur += output_bytes) { + cur[filter_bytes + 1] = 255; + } + } + } + } - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j = 0; j < y; ++j) { + stbi_uc *cur = a->out + stride * j; + stbi_uc *in = a->out + stride * j + x * out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1;// scale grayscale values to 0..255 range - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; + if (depth == 4) { + for (k = x * img_n; k >= 2; k -= 2, ++in) { + *cur++ = scale * ((*in >> 4)); + *cur++ = scale * ((*in) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4)); + } else if (depth == 2) { + for (k = x * img_n; k >= 4; k -= 4, ++in) { + *cur++ = scale * ((*in >> 6)); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6)); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k = x * img_n; k >= 8; k -= 8, ++in) { + *cur++ = scale * ((*in >> 7)); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7)); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride * j; + if (img_n == 1) { + for (q = x - 1; q >= 0; --q) { + cur[q * 2 + 1] = 255; + cur[q * 2 + 0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q = x - 1; q >= 0; --q) { + cur[q * 4 + 3] = 255; + cur[q * 4 + 2] = cur[q * 3 + 2]; + cur[q * 4 + 1] = cur[q * 3 + 1]; + cur[q * 4 + 0] = cur[q * 3 + 0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16 *) cur; - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } + for (i = 0; i < x * y * out_n; ++i, cur16++, cur += 2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } - return 1; + return 1; } -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - if (!final) return stbi__err("outofmem", "Out of memory"); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p = 0; p < 7; ++p) { + int xorig[] = {0, 4, 0, 2, 0, 1, 0}; + int yorig[] = {0, 0, 4, 0, 2, 0, 1}; + int xspc[] = {8, 8, 4, 4, 2, 2, 1}; + int yspc[] = {8, 8, 8, 4, 4, 2, 2}; + int i, j, x, y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p] - 1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p] - 1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j = 0; j < y; ++j) { + for (i = 0; i < x; ++i) { + int out_y = j * yspc[p] + yorig[p]; + int out_x = i * xspc[p] + xorig[p]; + memcpy(final + out_y * a->s->img_x * out_bytes + out_x * out_bytes, + a->out + (j * x + i) * out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; - return 1; + return 1; } -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) { + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; } -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) { + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16 *) z->out; - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; } -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) { + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); - // between here and free(out) below, exitting would leak - temp_out = p; + // between here and free(out) below, exitting would leak + temp_out = p; - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; + if (pal_img_n == 3) { + for (i = 0; i < pixel_count; ++i) { + int n = orig[i] * 4; + p[0] = palette[n]; + p[1] = palette[n + 1]; + p[2] = palette[n + 2]; + p += 3; + } + } else { + for (i = 0; i < pixel_count; ++i) { + int n = orig[i] * 4; + p[0] = palette[n]; + p[1] = palette[n + 1]; + p[2] = palette[n + 2]; + p[3] = palette[n + 3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; - STBI_NOTUSED(len); + STBI_NOTUSED(len); - return 1; + return 1; } static int stbi__unpremultiply_on_load_global = 0; static int stbi__de_iphone_flag_global = 0; -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) { + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; } -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag_global = flag_true_if_should_convert; +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) { + stbi__de_iphone_flag_global = flag_true_if_should_convert; } #ifndef STBI_THREAD_LOCAL -#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global -#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global #else static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; -STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; - stbi__unpremultiply_on_load_set = 1; +STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) { + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; } -STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag_local = flag_true_if_should_convert; - stbi__de_iphone_flag_set = 1; +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) { + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; } -#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ - ? stbi__unpremultiply_on_load_local \ - : stbi__unpremultiply_on_load_global) -#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ - ? stbi__de_iphone_flag_local \ - : stbi__de_iphone_flag_global) -#endif // STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif// STBI_THREAD_LOCAL -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; +static void stbi__de_iphone(stbi__png *z) { + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } + if (s->img_out_n == 3) {// convert bgr to rgb + for (i = 0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i = 0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = (t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i = 0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } } -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) +#define STBI__PNG_TYPE(a, b, c, d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]={0}; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { + stbi_uc palette[1024], pal_img_n = 0; + stbi_uc has_trans = 0, tc[3] = {0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff = 0, idata_limit = 0, i, pal_len = 0; + int first = 1, k, interlace = 0, color = 0, is_iphone = 0; + stbi__context *s = z->s; - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; - if (!stbi__check_png_header(s)) return 0; + if (!stbi__check_png_header(s)) return 0; - if (scan == STBI__SCAN_type) return 1; + if (scan == STBI__SCAN_type) return 1; - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); - s->img_y = stbi__get32be(s); - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C', 'g', 'B', 'I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I', 'H', 'D', 'R'): { + int comp, filter; + if (!first) return stbi__err("multiple IHDR", "Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len", "Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large", "Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large", "Very large image (corrupt?)"); + z->depth = stbi__get8(s); + if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only", "PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); + if (color > 6) return stbi__err("bad ctype", "Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype", "Corrupt PNG"); + if (color == 3) pal_img_n = 3; + else if (color & 1) + return stbi__err("bad ctype", "Corrupt PNG"); + comp = stbi__get8(s); + if (comp) return stbi__err("bad comp method", "Corrupt PNG"); + filter = stbi__get8(s); + if (filter) return stbi__err("bad filter method", "Corrupt PNG"); + interlace = stbi__get8(s); + if (interlace > 1) return stbi__err("bad interlace method", "Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image", "Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large", "Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } + case STBI__PNG_TYPE('P', 'L', 'T', 'E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256 * 3) return stbi__err("invalid PLTE", "Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE", "Corrupt PNG"); + for (i = 0; i < pal_len; ++i) { + palette[i * 4 + 0] = stbi__get8(s); + palette[i * 4 + 1] = stbi__get8(s); + palette[i * 4 + 2] = stbi__get8(s); + palette[i * 4 + 3] = 255; + } + break; + } - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } + case STBI__PNG_TYPE('t', 'R', 'N', 'S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT", "Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { + s->img_n = 4; + return 1; + } + if (pal_len == 0) return stbi__err("tRNS before PLTE", "Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len", "Corrupt PNG"); + pal_img_n = 4; + for (i = 0; i < c.length; ++i) + palette[i * 4 + 3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha", "Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n * 2) return stbi__err("bad tRNS len", "Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16) stbi__get16be(s);// copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth];// non 8-bit images will be larger + } + } + break; + } - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } + case STBI__PNG_TYPE('I', 'D', 'A', 'T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE", "Corrupt PNG"); + if (scan == STBI__SCAN_header) { + s->img_n = pal_img_n; + return 1; + } + if ((int) (ioff + c.length) < (int) ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata + ioff, c.length)) return stbi__err("outofdata", "Corrupt PNG"); + ioff += c.length; + break; + } - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - return 1; - } + case STBI__PNG_TYPE('I', 'E', 'N', 'D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT", "Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8;// bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0;// zlib should set error + STBI_FREE(z->idata); + z->idata = NULL; + if ((req_comp == s->img_n + 1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n + 1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n;// record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); + z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { +#ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); +#endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } } -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth <= 8) - ri->bits_per_channel = 8; - else if (p->depth == 16) - ri->bits_per_channel = 16; - else - return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) { + void *result = NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); + p->out = NULL; + STBI_FREE(p->expanded); + p->expanded = NULL; + STBI_FREE(p->idata); + p->idata = NULL; - return result; + return result; } -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + stbi__png p; + p.s = s; + return stbi__do_png(&p, x, y, comp, req_comp, ri); } -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; +static int stbi__png_test(stbi__context *s) { + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; } -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) { + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind(p->s); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; } -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) { + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); } -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; +static int stbi__png_is16(stbi__context *s) { + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; } #endif // Microsoft/Windows BMP image #ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; +static int stbi__bmp_test_raw(stbi__context *s) { + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s);// discard filesize + stbi__get16le(s);// discard reserved + stbi__get16le(s);// discard reserved + stbi__get32le(s);// discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; } -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; +static int stbi__bmp_test(stbi__context *s) { + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; } // returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) { n += 16; z >>= 16; } - if (z >= 0x00100) { n += 8; z >>= 8; } - if (z >= 0x00010) { n += 4; z >>= 4; } - if (z >= 0x00004) { n += 2; z >>= 2; } - if (z >= 0x00002) { n += 1;/* >>= 1;*/ } - return n; +static int stbi__high_bit(unsigned int z) { + int n = 0; + if (z == 0) return -1; + if (z >= 0x10000) { + n += 16; + z >>= 16; + } + if (z >= 0x00100) { + n += 8; + z >>= 8; + } + if (z >= 0x00010) { + n += 4; + z >>= 4; + } + if (z >= 0x00004) { + n += 2; + z >>= 2; + } + if (z >= 0x00002) { n += 1; /* >>= 1;*/ } + return n; } -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; +static int stbi__bitcount(unsigned int a) { + a = (a & 0x55555555) + ((a >> 1) & 0x55555555);// max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333);// max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; } // extract an arbitrarily-aligned N-bit value (N=bits) // from v, and then make it 8-bits long and fractionally // extend it to full full range. -static int stbi__shiftsigned(unsigned int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +static int stbi__shiftsigned(unsigned int v, int shift, int bits) { + static unsigned int mul_table[9] = { + 0, + 0xff /*0b11111111*/, + 0x55 /*0b01010101*/, + 0x49 /*0b01001001*/, + 0x11 /*0b00010001*/, + 0x21 /*0b00100001*/, + 0x41 /*0b01000001*/, + 0x81 /*0b10000001*/, + 0x01 /*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, + 0, + 0, + 1, + 0, + 2, + 4, + 6, + 0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8 - bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; } typedef struct { - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; - int extra_read; + int bpp, offset, hsz; + unsigned int mr, mg, mb, ma, all_a; + int extra_read; } stbi__bmp_data; -static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) -{ - // BI_BITFIELDS specifies masks explicitly, don't override - if (compress == 3) - return 1; +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) { + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; - if (compress == 0) { - if (info->bpp == 16) { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } else if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - // otherwise, use defaults, which is all-0 - info->mr = info->mg = info->mb = info->ma = 0; - } - return 1; - } - return 0; // error + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0;// if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0;// error } -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - info->extra_read = 14; +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) { + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s);// discard filesize + stbi__get16le(s);// discard reserved + stbi__get16le(s);// discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; - if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes - if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - stbi__bmp_set_mask_defaults(info, compress); - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->extra_read += 12; - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - // V4/V5 header - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs - stbi__bmp_set_mask_defaults(info, compress); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression");// this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3)// override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s);// discard color space + for (i = 0; i < 12; ++i) + stbi__get32le(s);// discard color space parameters + if (hsz == 124) { + stbi__get32le(s);// discard rendering intent + stbi__get32le(s);// discard offset of profile data + stbi__get32le(s);// discard size of profile data + stbi__get32le(s);// discard reserved + } + } + } + return (void *) 1; } -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + stbi_uc *out; + unsigned int mr = 0, mg = 0, mb = 0, ma = 0, all_a; + stbi_uc pal[256][4]; + int psize = 0, i, j, width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL;// error code already set - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large", "Very large image (corrupt?)"); - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - info.extra_read - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - info.extra_read - info.hsz) >> 2; - } - if (psize == 0) { - if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { - return stbi__errpuc("bad offset", "Corrupt BMP"); - } - } + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } + } - if (info.bpp == 24 && ma == 0xff000000) - s->img_n = 3; - else - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3)// we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n;// if they want monochrome, we'll post-convert - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - info.extra_read - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z = 0; + if (psize == 0 || psize > 256) { + STBI_FREE(out); + return stbi__errpuc("invalid", "Corrupt BMP"); + } + for (i = 0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) + width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) + width = s->img_x; + else { + STBI_FREE(out); + return stbi__errpuc("bad bpp", "Corrupt BMP"); + } + pad = (-width) & 3; + if (info.bpp == 1) { + for (j = 0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i = 0; i < (int) s->img_x; ++i) { + int color = (v >> bit_offset) & 0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i + 1 == (int) s->img_x) break; + if ((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j = 0; j < (int) s->img_y; ++j) { + for (i = 0; i < (int) s->img_x; i += 2) { + int v = stbi__get8(s), v2 = 0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i + 1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift = 0, gshift = 0, bshift = 0, ashift = 0, rcount = 0, gcount = 0, bcount = 0, acount = 0; + int z = 0; + int easy = 0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) + width = 2 * s->img_x; + else /* bpp = 32 and pad = 0 */ + width = 0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { + STBI_FREE(out); + return stbi__errpuc("bad masks", "Corrupt BMP"); + } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr) - 7; + rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg) - 7; + gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb) - 7; + bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma) - 7; + acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { + STBI_FREE(out); + return stbi__errpuc("bad masks", "Corrupt BMP"); + } + } + for (j = 0; j < (int) s->img_y; ++j) { + if (easy) { + for (i = 0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z + 2] = stbi__get8(s); + out[z + 1] = stbi__get8(s); + out[z + 0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i = 0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i = 4 * s->img_x * s->img_y - 1; i >= 0; i -= 4) + out[i] = 255; - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i]; p1[i] = p2[i]; p2[i] = t; - } - } - } + if (flip_vertically) { + stbi_uc t; + for (j = 0; j < (int) s->img_y >> 1; ++j) { + stbi_uc *p1 = out + j * s->img_x * target; + stbi_uc *p2 = out + (s->img_y - 1 - j) * s->img_x * target; + for (i = 0; i < (int) s->img_x * target; ++i) { + t = p1[i]; + p1[i] = p2[i]; + p2[i] = t; + } + } + } - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out;// stbi__convert_format frees input on failure + } - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; } #endif @@ -5658,341 +5789,327 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req // by Jonathan Dummer #ifndef STBI_NO_TGA // returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; - } +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int *is_rgb16) { + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch (bits_per_pixel) { + case 8: + return STBI_grey; + case 16: + if (is_grey) return STBI_grey_alpha; + // fallthrough + case 15: + if (is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24:// fallthrough + case 32: + return bits_per_pixel / 8; + default: + return 0; + } } -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) { + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s);// colormap type + if (tga_colormap_type > 1) { + stbi__rewind(s); + return 0;// only RGB or indexed allowed + } + tga_image_type = stbi__get8(s);// image type + if (tga_colormap_type == 1) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s, 4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s);// check bits per palette color entry + if ((sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi__rewind(s); + return 0; + } + stbi__skip(s, 4);// skip image x and y origin + tga_colormap_bpp = sz; + } else {// "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ((tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11)) { + stbi__rewind(s); + return 0;// only RGB or grey allowed, +/- RLE + } + stbi__skip(s, 9);// skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if (tga_w < 1) { + stbi__rewind(s); + return 0;// test width + } + tga_h = stbi__get16le(s); + if (tga_h < 1) { + stbi__rewind(s); + return 0;// test height + } + tga_bits_per_pixel = stbi__get8(s);// bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if (!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1;// seems to have passed everything } -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; +static int stbi__tga_test(stbi__context *s) { + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if (tga_color_type > 1) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if (tga_color_type == 1) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd;// colortype 1 demands image type 1 or 9 + stbi__skip(s, 4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ((sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32)) goto errorEnd; + stbi__skip(s, 4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ((sz != 2) && (sz != 3) && (sz != 10) && (sz != 11)) goto errorEnd;// only RGB or grey allowed, +/- RLE + stbi__skip(s, 9); // skip colormap specification and image x/y origin + } + if (stbi__get16le(s) < 1) goto errorEnd; // test width + if (stbi__get16le(s) < 1) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ((tga_color_type == 1) && (sz != 8) && (sz != 16)) goto errorEnd;// for colormapped images, bpp is size of an index + if ((sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32)) goto errorEnd; - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + res = 1;// if we got this far, everything's good and we can return 1 instead of 0 errorEnd: - stbi__rewind(s); - return res; + stbi__rewind(s); + return res; } // read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc *out) { + stbi__uint16 px = (stbi__uint16) stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc) ((r * 255) / 31); + out[1] = (stbi_uc) ((g * 255) / 31); + out[2] = (stbi_uc) ((b * 255) / 31); - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. } -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - STBI_NOTUSED(tga_x_origin); // @TODO - STBI_NOTUSED(tga_y_origin); // @TODO +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16 = 0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin);// @TODO + STBI_NOTUSED(tga_y_origin);// @TODO - if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large", "Very large image (corrupt?)"); - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); + // do a tiny bit of precessing + if (tga_image_type >= 8) { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + // If I'm paletted, then I'll use the number of bits from the palette + if (tga_indexed) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + if (!tga_comp)// shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + tga_data = (unsigned char *) stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset); - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - if (tga_palette_len == 0) { /* you have to have at least one entry! */ - STBI_FREE(tga_data); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } + if (!tga_indexed && !tga_is_RLE && !tga_rgb16) { + for (i = 0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height - i - 1 : i; + stbi_uc *tga_row = tga_data + row * tga_width * tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if (tga_indexed) { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start); + // load the palette + tga_palette = (unsigned char *) stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i = 0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i = 0; i < tga_width * tga_height; ++i) { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if (tga_is_RLE) { + if (RLE_count == 0) { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if (!RLE_repeating) { + read_next_pixel = 1; + } + } else { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if (read_next_pixel) { + // load however much data we did have + if (tga_indexed) { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if (pal_idx >= tga_palette_len) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx + j]; + } + } else if (tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + }// end of reading a pixel - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i * tga_comp + j] = raw_data[j]; - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if (tga_inverted) { + for (j = 0; j * 2 < tga_height; ++j) { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if (tga_palette != NULL) { + STBI_FREE(tga_palette); + } + } - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) { + unsigned char *tga_pixel = tga_data; + for (i = 0; i < tga_width * tga_height; ++i) { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - STBI_NOTUSED(tga_palette_start); - // OK, done - return tga_data; + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; } #endif @@ -6000,250 +6117,247 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB #ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; +static int stbi__psd_test(stbi__context *s) { + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; } -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) { + int count, nleft, len; - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0;// corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0;// corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } - return 1; + return 1; } -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w,h; - stbi_uc *out; - STBI_NOTUSED(ri); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w, h; + stbi_uc *out; + STBI_NOTUSED(ri); - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); + // Check identifier + if (stbi__get32be(s) != 0x38425053)// "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); + // Skip 6 reserved bytes. + stbi__skip(s, 6); - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); - if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large", "Very large image (corrupt?)"); - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s, stbi__get32be(s)); - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s)); - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s)); - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); - // Create the destination image. + // Create the destination image. - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w * h); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w * h; - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop - // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2); - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } + p = out + channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out + channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) {// output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out + channel; + if (bitdepth == 16) {// input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - } + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i = 0; i < w * h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4 * i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0] * ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1] * ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2] * ra + inv_a); + } + } + } else { + for (i = 0; i < w * h; ++i) { + unsigned char *pixel = out + 4 * i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0] * ra + inv_a); + pixel[1] = (unsigned char) (pixel[1] * ra + inv_a); + pixel[2] = (unsigned char) (pixel[2] * ra + inv_a); + } + } + } + } - // convert to desired output format - if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out;// stbi__convert_format frees input on failure + } - if (comp) *comp = 4; - *y = h; - *x = w; + if (comp) *comp = 4; + *y = h; + *x = w; - return out; + return out; } #endif @@ -6255,216 +6369,208 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ #ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; +static int stbi__pic_is4(stbi__context *s, const char *str) { + int i; + for (i = 0; i < 4; ++i) + if (stbi__get8(s) != (stbi_uc) str[i]) + return 0; - return 1; + return 1; } -static int stbi__pic_test_core(stbi__context *s) -{ - int i; +static int stbi__pic_test_core(stbi__context *s) { + int i; - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; + if (!stbi__pic_is4(s, "\x53\x80\xF6\x34")) + return 0; - for(i=0;i<84;++i) - stbi__get8(s); + for (i = 0; i < 84; ++i) + stbi__get8(s); - if (!stbi__pic_is4(s,"PICT")) - return 0; + if (!stbi__pic_is4(s, "PICT")) + return 0; - return 1; + return 1; } typedef struct { - stbi_uc size,type,channel; + stbi_uc size, type, channel; } stbi__pic_packet; -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) { + int mask = 0x80, i; - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } + for (i = 0; i < 4; ++i, mask >>= 1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file", "PIC file too short"); + dest[i] = stbi__get8(s); + } + } - return dest; + return dest; } -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; +static void stbi__copyval(int channel, stbi_uc *dest, const stbi_uc *src) { + int mask = 0x80, i; - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; + for (i = 0; i < 4; ++i, mask >>= 1) + if (channel & mask) + dest[i] = src[i]; } -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; +static stbi_uc *stbi__pic_load_core(stbi__context *s, int width, int height, int *comp, stbi_uc *result) { + int act_comp = 0, num_packets = 0, y, chained; + stbi__pic_packet packets[10]; - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); + if (num_packets == sizeof(packets) / sizeof(packets[0])) + return stbi__errpuc("bad format", "too many packets"); - packet = &packets[num_packets++]; + packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); - act_comp |= packet->channel; + act_comp |= packet->channel; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); + if (stbi__at_eof(s)) return stbi__errpuc("bad file", "file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format", "packet isn't 8bpp"); + } while (chained); - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + *comp = (act_comp & 0x10 ? 4 : 3);// has alpha channel? - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); + switch (packet->type) { + default: + return stbi__errpuc("bad format", "packet has bad compression type"); - case 0: {//uncompressed - int x; + case 0: {//uncompressed + int x; - for(x=0;xchannel,dest)) - return 0; - break; - } + for (x = 0; x < width; ++x, dest += 4) + if (!stbi__readval(s, packet->channel, dest)) + return 0; + break; + } - case 1://Pure RLE - { - int left=width, i; + case 1://Pure RLE + { + int left = width, i; - while (left>0) { - stbi_uc count,value[4]; + while (left > 0) { + stbi_uc count, value[4]; - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + count = stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file", "file too short (pure read count)"); - if (count > left) - count = (stbi_uc) left; + if (count > left) + count = (stbi_uc) left; - if (!stbi__readval(s,packet->channel,value)) return 0; + if (!stbi__readval(s, packet->channel, value)) return 0; - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; + for (i = 0; i < count; ++i, dest += 4) + stbi__copyval(packet->channel, dest, value); + left -= count; + } + } break; - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + case 2: {//Mixed RLE + int left = width; + while (left > 0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file", "file too short (mixed read count)"); - if (count >= 128) { // Repeated - stbi_uc value[4]; + if (count >= 128) {// Repeated + stbi_uc value[4]; - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); + if (count == 128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file", "scanline overrun"); - if (!stbi__readval(s,packet->channel,value)) - return 0; + if (!stbi__readval(s, packet->channel, value)) + return 0; - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); + for (i = 0; i < count; ++i, dest += 4) + stbi__copyval(packet->channel, dest, value); + } else {// Raw + ++count; + if (count > left) return stbi__errpuc("bad file", "scanline overrun"); - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } + for (i = 0; i < count; ++i, dest += 4) + if (!stbi__readval(s, packet->channel, dest)) + return 0; + } + left -= count; + } + break; + } + } + } + } - return result; + return result; } -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); +static void *stbi__pic_load(stbi__context *s, int *px, int *py, int *comp, int req_comp, stbi__result_info *ri) { + stbi_uc *result; + int i, x, y, internal_comp; + STBI_NOTUSED(ri); - if (!comp) comp = &internal_comp; + if (!comp) comp = &internal_comp; - for (i=0; i<92; ++i) - stbi__get8(s); + for (i = 0; i < 92; ++i) + stbi__get8(s); - x = stbi__get16be(s); - y = stbi__get16be(s); + x = stbi__get16be(s); + y = stbi__get16be(s); - if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large", "Very large image (corrupt?)"); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + if (stbi__at_eof(s)) return stbi__errpuc("bad file", "file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' + stbi__get32be(s);//skip `ratio' + stbi__get16be(s);//skip `fields' + stbi__get16be(s);//skip `pad' - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); - if (!result) return stbi__errpuc("outofmem", "Out of memory"); - memset(result, 0xff, x*y*4); + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x * y * 4); - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); + if (!stbi__pic_load_core(s, x, y, comp, result)) { + STBI_FREE(result); + result = 0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result = stbi__convert_format(result, 4, req_comp, x, y); - return result; + return result; } -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; +static int stbi__pic_test(stbi__context *s) { + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; } #endif @@ -6474,531 +6580,519 @@ static int stbi__pic_test(stbi__context *s) #ifndef STBI_NO_GIF typedef struct { - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; } stbi__gif_lzw; typedef struct { - int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; + int w, h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background;// The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; } stbi__gif; -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; +static int stbi__gif_test_raw(stbi__context *s) { + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; } -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; +static int stbi__gif_test(stbi__context *s) { + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; } -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) { + int i; + for (i = 0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } } -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) { + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; - if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large", "Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large", "Very large image (corrupt?)"); - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + if (comp != 0) *comp = 4;// can't actually tell whether it's 3 or 4 until we parse the comments - if (is_info) return 1; + if (is_info) return 1; - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + if (g->flags & 0x80) + stbi__gif_parse_colortable(s, g->pal, 2 << (g->flags & 7), -1); - return 1; + return 1; } -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!g) return stbi__err("outofmem", "Out of memory"); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { + stbi__gif *g = (stbi__gif *) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind(s); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; } -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { + stbi_uc *p, *c; + int idx; - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); - if (g->cur_y >= g->max_y) return; + if (g->cur_y >= g->max_y) return; - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) {// don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } } -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) { + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } - // support no starting clear code - avail = clear+2; - oldcode = -1; + // support no starting clear code + avail = clear + 2; + oldcode = -1; - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } + len = 0; + for (;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s);// start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) {// clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) {// end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s, len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - stbi__out_gif_code(g, (stbi__uint16) code); + stbi__out_gif_code(g, (stbi__uint16) code); - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } } // this function is designed to support animated gifs, although stb_image doesn't support it // two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - STBI_NOTUSED(req_comp); +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) - return stbi__errpuc("too large", "GIF image is too large"); - pcount = g->w * g->h; - g->out = (stbi_uc *) stbi__malloc(4 * pcount); - g->background = (stbi_uc *) stbi__malloc(4 * pcount); - g->history = (stbi_uc *) stbi__malloc(pcount); - if (!g->out || !g->background || !g->history) - return stbi__errpuc("outofmem", "Out of memory"); + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp, 0)) return 0;// stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); - // image is treated as "transparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to the color that was there the previous frame. - memset(g->out, 0x00, 4 * pcount); - memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) - memset(g->history, 0x00, pcount); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispose of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount);// state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } + if ((dispose == 3) && (two_back == 0)) { + dispose = 2;// if I don't have an image to revert back to, default to the old background + } - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } + if (dispose == 3) {// use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy(&g->out[pi * 4], &two_back[pi * 4], 4); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy(&g->out[pi * 4], &g->background[pi * 4], 4); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); - } + // background is what out is after the undoing of the previou frame; + memcpy(g->background, g->out, 4 * g->w * g->h); + } - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + // clear my history; + memset(g->history, 0x00, g->w * g->h);// pixels that were affected previous frame - for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; - // if the width of the specified rectangle is 0, that means - // we may not see *any* pixels or the image is malformed; - // to make sure this is caught, move the current y down to - // max_y (which is what out_gif_code checks). - if (w == 0) - g->cur_y = g->max_y; + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; - g->lflags = stbi__get8(s); + g->lflags = stbi__get8(s); - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } + if (g->lflags & 0x40) { + g->step = 8 * g->line_size;// first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s, g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); - o = stbi__process_gif_raster(s, g); - if (!o) return NULL; + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255;// just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy(&g->out[pi * 4], &g->pal[g->bgindex], 4); + } + } + } - return o; - } + return o; + } - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + case 0x21:// Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) {// Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s);// delay - 1/100th of a second, saving as 1/1000ths. - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers + case 0x3B: // gif stream termination code + return (stbi_uc *) s;// using '1' causes warning on some compilers - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } } -static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) -{ - STBI_FREE(g->out); - STBI_FREE(g->history); - STBI_FREE(g->background); +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) { + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); - if (out) STBI_FREE(out); - if (delays && *delays) STBI_FREE(*delays); - return stbi__errpuc("outofmem", "Out of memory"); + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); } -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - int out_size = 0; - int delays_size = 0; +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; - STBI_NOTUSED(out_size); - STBI_NOTUSED(delays_size); + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0;// end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; - if (out) { - void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); - if (!tmp) - return stbi__load_gif_main_outofmem(&g, out, delays); - else { - out = (stbi_uc*) tmp; - out_size = layers * stride; - } + if (out) { + void *tmp = (stbi_uc *) STBI_REALLOC_SIZED(out, out_size, layers * stride); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc *) tmp; + out_size = layers * stride; + } - if (delays) { - int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); - if (!new_delays) - return stbi__load_gif_main_outofmem(&g, out, delays); - *delays = new_delays; - delays_size = layers * sizeof(int); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (!out) - return stbi__load_gif_main_outofmem(&g, out, delays); - out_size = layers * stride; - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - if (!*delays) - return stbi__load_gif_main_outofmem(&g, out, delays); - delays_size = layers * sizeof(int); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } + if (delays) { + int *new_delays = (int *) STBI_REALLOC_SIZED(*delays, delays_size, sizeof(int) * layers); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc *) stbi__malloc(layers * stride); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int *) stbi__malloc(layers * sizeof(int)); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy(out + ((layers - 1) * stride), u, stride); + if (layers >= 2) { + two_back = out - 2 * stride; + } - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } } -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - STBI_NOTUSED(ri); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0;// end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } else if (g.out) { - // if there was an error and we allocated an image buffer, free it! - STBI_FREE(g.out); - } + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); - return u; + return u; } -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) { + return stbi__gif_info_raw(s, x, y, comp); } #endif @@ -7006,397 +7100,403 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) // Radiance RGBE HDR loader // originally by Nicolas Schulz #ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; +static int stbi__hdr_test_core(stbi__context *s, const char *signature) { + int i; + for (i = 0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; } -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; +static int stbi__hdr_test(stbi__context *s) { + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if (!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; } -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) { + int len = 0; + char c = '\0'; - c = (char) stbi__get8(z); + c = (char) stbi__get8(z); - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN - 1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } - buffer[len] = 0; - return buffer; + buffer[len] = 0; + return buffer; } -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) { + if (input[3] != 0) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int) (128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: + output[3] = 1; /* fallthrough */ + case 3: + output[0] = output[1] = output[2] = 0; + break; + case 2: + output[1] = 1; /* fallthrough */ + case 1: + output[0] = 0; + break; + } + } } -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1, c2, z; + const char *headerToken; + STBI_NOTUSED(ri); - // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); + // Check identifier + headerToken = stbi__hdr_gettoken(s, buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } + // Parse header + for (;;) { + token = stbi__hdr_gettoken(s, buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s, buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); - if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large", "Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large", "Very large image (corrupt?)"); - *x = width; - *y = height; + *x = width; + *y = height; - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; + // Load image data + // image data is stored as some number of sca + if (width < 8 || width >= 32768) { + // Read flat data + for (j = 0; j < height; ++j) { + for (i = 0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop;// yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { + STBI_FREE(hdr_data); + STBI_FREE(scanline); + return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); + } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { + STBI_FREE(hdr_data); + STBI_FREE(scanline); + return stbi__errpf("corrupt", "bad RLE data in HDR"); + } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { + STBI_FREE(hdr_data); + STBI_FREE(scanline); + return stbi__errpf("corrupt", "bad RLE data in HDR"); + } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i = 0; i < width; ++i) + stbi__hdr_convert(hdr_data + (j * width + i) * req_comp, scanline + i * 4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } - return hdr_data; + return hdr_data; } -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) { + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } + if (stbi__hdr_test(s) == 0) { + stbi__rewind(s); + return 0; + } - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } + for (;;) { + token = stbi__hdr_gettoken(s, buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; + if (!valid) { + stbi__rewind(s); + return 0; + } + token = stbi__hdr_gettoken(s, buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind(s); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind(s); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; } -#endif // STBI_NO_HDR +#endif// STBI_NO_HDR #ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) { + void *p; + stbi__bmp_data info; - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - if (p == NULL) { - stbi__rewind( s ); - return 0; - } - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) { - if (info.bpp == 24 && info.ma == 0xff000000) - *comp = 3; - else - *comp = info.ma ? 4 : 3; - } - return 1; + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind(s); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; } #endif #ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) { + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind(s); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind(s); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind(s); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind(s); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind(s); + return 0; + } + *comp = 4; + return 1; } -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - STBI_NOTUSED(stbi__get32be(s)); - STBI_NOTUSED(stbi__get32be(s)); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; +static int stbi__psd_is16(stbi__context *s) { + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind(s); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind(s); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind(s); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind(s); + return 0; + } + return 1; } #endif #ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained,dummy; - stbi__pic_packet packets[10]; +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) { + int act_comp = 0, num_packets = 0, chained, dummy; + stbi__pic_packet packets[10]; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } + if (!stbi__pic_is4(s, "\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } - stbi__skip(s, 88); + stbi__skip(s, 88); - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind(s); + return 0; + } + if ((*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind(s); + return 0; + } - stbi__skip(s, 8); + stbi__skip(s, 8); - do { - stbi__pic_packet *packet; + do { + stbi__pic_packet *packet; - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; + if (num_packets == sizeof(packets) / sizeof(packets[0])) + return 0; - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); + if (stbi__at_eof(s)) { + stbi__rewind(s); + return 0; + } + if (packet->size != 8) { + stbi__rewind(s); + return 0; + } + } while (chained); - *comp = (act_comp & 0x10 ? 4 : 3); + *comp = (act_comp & 0x10 ? 4 : 3); - return 1; + return 1; } #endif @@ -7413,262 +7513,244 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; +static int stbi__pnm_test(stbi__context *s) { + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + return 1; } -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + stbi_uc *out; + STBI_NOTUSED(ri); - ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); - if (ri->bits_per_channel == 0) - return 0; + ri->bits_per_channel = stbi__pnm_info(s, (int *) &s->img_x, (int *) &s->img_y, (int *) &s->img_n); + if (ri->bits_per_channel == 0) + return 0; - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large", "Very large image (corrupt?)"); - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; - if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) - return stbi__errpuc("too large", "PNM too large"); + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); - out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out;// stbi__convert_format frees input on failure + } + return out; } -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +static int stbi__pnm_isspace(char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; } -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) { + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); - if (stbi__at_eof(s) || *c != '#') - break; + if (stbi__at_eof(s) || *c != '#') + break; - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r') + *c = (char) stbi__get8(s); + } } -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; +static int stbi__pnm_isdigit(char c) { + return c >= '0' && c <= '9'; } -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; +static int stbi__pnm_getinteger(stbi__context *s, char *c) { + int value = 0; - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value * 10 + (*c - '0'); + *c = (char) stbi__get8(s); + } - return value; + return value; } -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) { + int maxv, dummy; + char c, p, t; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; - stbi__rewind(s); + stbi__rewind(s); - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; - } + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + *comp = (t == '6') ? 3 : 1;// '5' is 1-component .pgm; '6' is 3-component .ppm - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); + *x = stbi__pnm_getinteger(s, &c);// read width + stbi__pnm_skip_whitespace(s, &c); - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); + *y = stbi__pnm_getinteger(s, &c);// read height + stbi__pnm_skip_whitespace(s, &c); - maxv = stbi__pnm_getinteger(s, &c); // read max value - if (maxv > 65535) - return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); - else if (maxv > 255) - return 16; - else - return 8; + maxv = stbi__pnm_getinteger(s, &c);// read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; } -static int stbi__pnm_is16(stbi__context *s) -{ - if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) - return 1; - return 0; +static int stbi__pnm_is16(stbi__context *s) { + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; } #endif -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) { +#ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; +#endif - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif +#ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; +#endif - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif +#ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; +#endif - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif +#ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; +#endif - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif +#ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; +#endif - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif +#ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; +#endif - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif +#ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; +#endif - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif +#ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; +#endif - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +// test tga last because it's a crappy test! +#ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; +#endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); } -static int stbi__is_16_main(stbi__context *s) -{ - #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; - #endif +static int stbi__is_16_main(stbi__context *s) { +#ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; +#endif - #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; - #endif +#ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; +#endif - #ifndef STBI_NO_PNM - if (stbi__pnm_is16(s)) return 1; - #endif - return 0; +#ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; +#endif + return 0; } #ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) { + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; } -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) { + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s, x, y, comp); + fseek(f, pos, SEEK_SET); + return r; } -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; +STBIDEF int stbi_is_16_bit(char const *filename) { + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; } -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; +STBIDEF int stbi_is_16_bit_from_file(FILE *f) { + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f, pos, SEEK_SET); + return r; } -#endif // !STBI_NO_STDIO +#endif// !STBI_NO_STDIO -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) { + stbi__context s; + stbi__start_mem(&s, buffer, len); + return stbi__info_main(&s, x, y, comp); } -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) { + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s, x, y, comp); } -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) { + stbi__context s; + stbi__start_mem(&s, buffer, len); + return stbi__is_16_main(&s); } -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) { + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); } -#endif // STB_IMAGE_IMPLEMENTATION +#endif// STB_IMAGE_IMPLEMENTATION /* revision history: From 6fb0ebdc7acd08eb4da769b5530b983e2545455c Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Tue, 2 Aug 2022 10:17:50 +0200 Subject: [PATCH 06/18] [sdl] C implementation. Atlas page loading incomplete. --- spine-sdl/CMakeLists.txt | 2 +- spine-sdl/src/spine-sdl-c.c | 207 ++++++++++++++++++++++++++++++++ spine-sdl/src/spine-sdl-c.h | 66 ++++++++++ spine-sdl/src/spine-sdl-cpp.cpp | 13 +- spine-sdl/src/spine-sdl-cpp.h | 1 - 5 files changed, 281 insertions(+), 8 deletions(-) diff --git a/spine-sdl/CMakeLists.txt b/spine-sdl/CMakeLists.txt index bd84feea2..85cfae127 100644 --- a/spine-sdl/CMakeLists.txt +++ b/spine-sdl/CMakeLists.txt @@ -16,7 +16,7 @@ FetchContent_MakeAvailable(SDL) include_directories(src) add_library(spine-sdl-c STATIC src/spine-sdl-c.c src/spine-sdl-c.h src/stb_image.h) -target_link_libraries(spine-sdl-c LINK_PUBLIC spine-c) +target_link_libraries(spine-sdl-c LINK_PUBLIC SDL2 spine-c) install(TARGETS spine-sdl-c DESTINATION dist/lib) install(FILES src/spine-sdl-c.h src/stb_image.h DESTINATION dist/include) diff --git a/spine-sdl/src/spine-sdl-c.c b/spine-sdl/src/spine-sdl-c.c index e69de29bb..af3cf3875 100644 --- a/spine-sdl/src/spine-sdl-c.c +++ b/spine-sdl/src/spine-sdl-c.c @@ -0,0 +1,207 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated September 24, 2021. Replaces all prior versions. + * + * Copyright (c) 2013-2021, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, + * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include "spine-sdl-c.h" +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION + +#include + +spSkeletonDrawable *spSkeletonDrawable_create(spSkeletonData *skeletonData, spAnimationStateData *animationStateData) { + spSkeletonDrawable *self = NEW(spSkeletonDrawable); + self->skeleton = spSkeleton_create(skeletonData); + self->animationState = spAnimationState_create(animationStateData); + self->sdlIndices = spIntArray_create(12); + self->sdlVertices = spSdlVertexArray_create(12); + self->worldVertices = spFloatArray_create(12); + self->clipper = spSkeletonClipping_create(); + return self; +} + +void spSkeletonDrawable_dispose(spSkeletonDrawable *self) { + spSkeleton_dispose(self->skeleton); + spAnimationState_dispose(self->animationState); + spIntArray_dispose(self->sdlIndices); + spSdlVertexArray_dispose(self->sdlVertices); + spFloatArray_dispose(self->worldVertices); + spSkeletonClipping_dispose(self->clipper); + FREE(self); +} + +void spSkeletonDrawable_update(spSkeletonDrawable *self, float delta) { + spAnimationState_update(self->animationState, delta); + spAnimationState_apply(self->animationState, self->skeleton); + spSkeleton_updateWorldTransform(self->skeleton); +} + +void spSkeletonDrawable_draw(spSkeletonDrawable *self, struct SDL_Renderer *renderer) { + static unsigned short quadIndices[] = { 0, 1, 2, 2, 3, 0 }; + spSkeleton *skeleton = self->skeleton; + spSkeletonClipping *clipper = self->clipper; + SDL_Texture *texture; + SDL_Vertex sdlVertex; + for (unsigned i = 0; i < skeleton->slotsCount; ++i) { + spSlot *slot = skeleton->drawOrder[i]; + spAttachment *attachment = slot->attachment; + if (!attachment) continue; + + // Early out if the slot color is 0 or the bone is not active + if (slot->color.a == 0 || !slot->bone->active) { + spSkeletonClipping_clipEnd(clipper, slot); + continue; + } + + spFloatArray *vertices = self->worldVertices; + int verticesCount = 0; + float *uvs = NULL; + unsigned short *indices; + int indicesCount = 0; + spColor *attachmentColor = NULL; + + if (attachment->type == SP_ATTACHMENT_REGION) { + spRegionAttachment *region = (spRegionAttachment *) attachment; + attachmentColor = ®ion->color; + + // Early out if the slot color is 0 + if (attachmentColor->a == 0) { + spSkeletonClipping_clipEnd(clipper, slot); + continue; + } + + spFloatArray_setSize(vertices, 8); + spRegionAttachment_computeWorldVertices(region, slot, vertices->items, 0, 2); + verticesCount = 4; + uvs = ®ion->uvs; + indices = &quadIndices; + indicesCount = 6; + texture = (SDL_Texture *) ((spAtlasRegion*)region->rendererObject)->page->rendererObject; + } else if (attachment->type == SP_ATTACHMENT_MESH) { + spMeshAttachment *mesh = (spMeshAttachment *) attachment; + attachmentColor = &mesh->color; + + // Early out if the slot color is 0 + if (attachmentColor->a == 0) { + spSkeletonClipping_clipEnd(clipper, slot); + continue; + } + + spFloatArray_setSize(vertices, mesh->super.worldVerticesLength); + spVertexAttachment_computeWorldVertices(SUPER(mesh), slot, 0, mesh->super.worldVerticesLength, vertices, 0, 2); + verticesCount = mesh->super.worldVerticesLength >> 1; + uvs = mesh->uvs; + indices = mesh->triangles; + indicesCount = mesh->trianglesCount; + texture = (SDL_Texture *) ((spAtlasRegion*)mesh->rendererObject)->page->rendererObject; + } else if (attachment->type == SP_ATTACHMENT_CLIPPING) { + spClippingAttachment *clip = (spClippingAttachment *) slot->attachment; + spSkeletonClipping_clipStart(clipper, slot, clip); + continue; + } else + continue; + + Uint8 r = (Uint8)(skeleton->color.r * slot->color.r * attachmentColor->r * 255); + Uint8 g = (Uint8)(skeleton->color.g * slot->color.g * attachmentColor->g * 255); + Uint8 b = (Uint8)(skeleton->color.b * slot->color.b * attachmentColor->b * 255); + Uint8 a = (Uint8)(skeleton->color.a * slot->color.a * attachmentColor->a * 255); + sdlVertex.color.r = r; + sdlVertex.color.g = g; + sdlVertex.color.b = b; + sdlVertex.color.a = a; + + if (spSkeletonClipping_isClipping(clipper)) { + spSkeletonClipping_clipTriangles(clipper, vertices, verticesCount << 1, indices, indicesCount, uvs, 2); + vertices = clipper->clippedVertices->items; + verticesCount = clipper->clippedVertices->size >> 1; + uvs = clipper->clippedUVs->items; + indices = clipper->clippedTriangles->items; + indicesCount = clipper->clippedTriangles->size; + } + + spSdlVertexArray_clear(self->sdlVertices); + for (int ii = 0; ii < verticesCount << 1; ii += 2) { + sdlVertex.position.x = vertices->items[ii]; + sdlVertex.position.y = vertices->items[ii + 1]; + sdlVertex.tex_coord.x = uvs[ii]; + sdlVertex.tex_coord.y = uvs[ii + 1]; + spSdlVertexArray_add(self->sdlVertices, sdlVertex); + } + spIntArray_clear(self->sdlIndices); + for (int ii = 0; ii < (int) indicesCount; ii++) + spIntArray_add(self->sdlIndices, indices[ii]); + + switch (slot->data->blendMode) { + case SP_BLEND_MODE_NORMAL: + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + break; + case SP_BLEND_MODE_MULTIPLY: + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD); + break; + case SP_BLEND_MODE_ADDITIVE: + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD); + break; + case SP_BLEND_MODE_SCREEN: + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + break; + } + + SDL_RenderGeometry(renderer, texture, self->sdlVertices->items, self->sdlVertices->size, self->sdlIndices->items, + indicesCount); + spSkeletonClipping_clipEnd(clipper, slot); + } + spSkeletonClipping_clipEnd2(clipper); +} + +void _spAtlasPage_createTexture(spAtlasPage *self, const char *path) { + int width, height, components; + stbi_uc *imageData = stbi_load(path, &width, &height, &components, 4); + if (!imageData) return; + SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, + height); + if (!texture) { + stbi_image_free(imageData); + return; + } + if (SDL_UpdateTexture(texture, NULL, imageData, width * 4)) { + stbi_image_free(imageData); + return; + } + stbi_image_free(imageData); + return; +} + +void _spAtlasPage_disposeTexture(spAtlasPage *self) { + SDL_DestroyTexture((SDL_Texture *) self->rendererObject); +} + +char *_spUtil_readFile(const char *path, int *length) { + return _spReadFile(path, length); +} \ No newline at end of file diff --git a/spine-sdl/src/spine-sdl-c.h b/spine-sdl/src/spine-sdl-c.h index e69de29bb..77c30ecf4 100644 --- a/spine-sdl/src/spine-sdl-c.h +++ b/spine-sdl/src/spine-sdl-c.h @@ -0,0 +1,66 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated September 24, 2021. Replaces all prior versions. + * + * Copyright (c) 2013-2021, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, + * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef SPINE_SDL +#define SPINE_SDL + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct SDL_Renderer; +struct SDL_Texture; +struct SDL_Vertex; +_SP_ARRAY_DECLARE_TYPE(spSdlVertexArray, struct SDL_Vertex); + +typedef struct spSkeletonDrawable { + spSkeleton *skeleton; + spAnimationState *animationState; + + spSkeletonClipping *clipper; + spFloatArray *worldVertices; + spSdlVertexArray *sdlVertices; + spIntArray *sdlIndices; +} spSkeletonDrawable; + +SP_API spSkeletonDrawable *spSkeletonDrawable_create(spSkeletonData *skeletonData, spAnimationStateData *animationStateData); + +SP_API void spSkeletonDrawable_destroy(spSkeletonDrawable *self); + +SP_API void spSkeletonDrawable_update(spSkeletonDrawable *self, float delta); + +SP_API void spSkeletonDrawable_draw(spSkeletonDrawable *self, struct SDL_Renderer *renderer); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/spine-sdl/src/spine-sdl-cpp.cpp b/spine-sdl/src/spine-sdl-cpp.cpp index 6a48c8e56..321aad8af 100644 --- a/spine-sdl/src/spine-sdl-cpp.cpp +++ b/spine-sdl/src/spine-sdl-cpp.cpp @@ -43,12 +43,6 @@ SkeletonDrawable::SkeletonDrawable(SkeletonData *skeletonData, AnimationStateDat ownsAnimationStateData = animationStateData == 0; if (ownsAnimationStateData) animationStateData = new (__FILE__, __LINE__) AnimationStateData(skeletonData); animationState = new (__FILE__, __LINE__) AnimationState(animationStateData); - quadIndices.add(0); - quadIndices.add(1); - quadIndices.add(2); - quadIndices.add(2); - quadIndices.add(3); - quadIndices.add(0); } SkeletonDrawable::~SkeletonDrawable() { @@ -64,6 +58,13 @@ void SkeletonDrawable::update(float delta) { } void SkeletonDrawable::draw(SDL_Renderer *renderer) { + Vector quadIndices; + quadIndices.add(0); + quadIndices.add(1); + quadIndices.add(2); + quadIndices.add(2); + quadIndices.add(3); + quadIndices.add(0); SDL_Texture *texture; SDL_Vertex sdlVertex; for (unsigned i = 0; i < skeleton->getSlots().size(); ++i) { diff --git a/spine-sdl/src/spine-sdl-cpp.h b/spine-sdl/src/spine-sdl-cpp.h index 97d34bd23..a2843f5d4 100644 --- a/spine-sdl/src/spine-sdl-cpp.h +++ b/spine-sdl/src/spine-sdl-cpp.h @@ -53,7 +53,6 @@ namespace spine { bool ownsAnimationStateData; SkeletonClipping clipper; Vector worldVertices; - Vector quadIndices; Vector sdlVertices; Vector sdlIndices; }; From 2b69bb2af5a3190c760ef603f4989822e5e09a95 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Tue, 2 Aug 2022 10:45:32 +0200 Subject: [PATCH 07/18] [sdl] Fix up texture loader in C implementation. --- spine-c/spine-c/include/spine/Array.h | 59 ++++++++++++++++++ spine-sdl/example/main.c | 89 ++++++++++++++++++++++++++- spine-sdl/example/main.cpp | 29 +++++++++ spine-sdl/src/spine-sdl-c.c | 20 +++--- spine-sdl/src/spine-sdl-c.h | 2 +- spine-sdl/src/stb_image.h | 2 +- 6 files changed, 189 insertions(+), 12 deletions(-) diff --git a/spine-c/spine-c/include/spine/Array.h b/spine-c/spine-c/include/spine/Array.h index 9908f4f28..e4d98fd7a 100644 --- a/spine-c/spine-c/include/spine/Array.h +++ b/spine-c/spine-c/include/spine/Array.h @@ -118,6 +118,65 @@ extern "C" { return self->items[self->size - 1]; \ } +#define _SP_ARRAY_IMPLEMENT_TYPE_NO_CONTAINS(name, itemType) \ + name* name##_create(int initialCapacity) { \ + name* array = CALLOC(name, 1); \ + array->size = 0; \ + array->capacity = initialCapacity; \ + array->items = CALLOC(itemType, initialCapacity); \ + return array; \ + } \ + void name##_dispose(name* self) { \ + FREE(self->items); \ + FREE(self); \ + } \ + void name##_clear(name* self) { \ + self->size = 0; \ + } \ + name* name##_setSize(name* self, int newSize) { \ + self->size = newSize; \ + if (self->capacity < newSize) { \ + self->capacity = MAX(8, (int)(self->size * 1.75f)); \ + self->items = REALLOC(self->items, itemType, self->capacity); \ + } \ + return self; \ + } \ + void name##_ensureCapacity(name* self, int newCapacity) { \ + if (self->capacity >= newCapacity) return; \ + self->capacity = newCapacity; \ + self->items = REALLOC(self->items, itemType, self->capacity); \ + } \ + void name##_add(name* self, itemType value) { \ + if (self->size == self->capacity) { \ + self->capacity = MAX(8, (int)(self->size * 1.75f)); \ + self->items = REALLOC(self->items, itemType, self->capacity); \ + } \ + self->items[self->size++] = value; \ + } \ + void name##_addAll(name* self, name* other) { \ + int i = 0; \ + for (; i < other->size; i++) { \ + name##_add(self, other->items[i]); \ + } \ + } \ + void name##_addAllValues(name* self, itemType* values, int offset, int count) { \ + int i = offset, n = offset + count; \ + for (; i < n; i++) { \ + name##_add(self, values[i]); \ + } \ + } \ + void name##_removeAt(name* self, int index) { \ + self->size--; \ + memmove(self->items + index, self->items + index + 1, sizeof(itemType) * (self->size - index)); \ + } \ + itemType name##_pop(name* self) { \ + itemType item = self->items[--self->size]; \ + return item; \ + } \ + itemType name##_peek(name* self) { \ + return self->items[self->size - 1]; \ + } + _SP_ARRAY_DECLARE_TYPE(spFloatArray, float) _SP_ARRAY_DECLARE_TYPE(spIntArray, int) diff --git a/spine-sdl/example/main.c b/spine-sdl/example/main.c index ffc6dabc2..07cafe8f2 100644 --- a/spine-sdl/example/main.c +++ b/spine-sdl/example/main.c @@ -1,3 +1,88 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated September 24, 2021. Replaces all prior versions. + * + * Copyright (c) 2013-2021, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, + * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include +#include + int main() { - return 0; -} \ No newline at end of file + if (SDL_Init(SDL_INIT_VIDEO)) { + printf("Error: %s", SDL_GetError()); + return -1; + } + SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, 0); + if (!window) { + printf("Error: %s", SDL_GetError()); + return -1; + } + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + if (!renderer) { + printf("Error: %s", SDL_GetError()); + return -1; + } + + spAtlas *atlas = spAtlas_createFromFile("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas", renderer); + spSkeletonJson *json = spSkeletonJson_create(atlas); + json->scale = 0.5f; + spSkeletonData *skeletonData = spSkeletonJson_readSkeletonDataFile(json, "/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.json"); + spAnimationStateData *animationStateData = spAnimationStateData_create(skeletonData); + spSkeletonDrawable *drawable = spSkeletonDrawable_create(skeletonData, animationStateData); + + drawable->skeleton->x = 400; drawable->skeleton->y = 500; + spSkeleton_setToSetupPose(drawable->skeleton); + spSkeletonDrawable_update(drawable, 0); + spAnimationState_setAnimationByName(drawable->animationState, 0, "run", -1); + + int quit = 0; + uint64_t lastFrameTime = SDL_GetPerformanceCounter(); + while (!quit) { + SDL_Event event; + while (SDL_PollEvent(&event) != 0) { + if (event.type == SDL_QUIT) { + quit = -1; + break; + } + } + + SDL_SetRenderDrawColor(renderer, 94, 93, 96, 255); + SDL_RenderClear(renderer); + + uint64_t now = SDL_GetPerformanceCounter(); + double deltaTime = (now - lastFrameTime) / (double) SDL_GetPerformanceFrequency(); + lastFrameTime = now; + + spSkeletonDrawable_update(drawable, deltaTime); + spSkeletonDrawable_draw(drawable, renderer); + + SDL_RenderPresent(renderer); + } + + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} diff --git a/spine-sdl/example/main.cpp b/spine-sdl/example/main.cpp index 7d4861679..cc1c7e096 100644 --- a/spine-sdl/example/main.cpp +++ b/spine-sdl/example/main.cpp @@ -1,3 +1,32 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated September 24, 2021. Replaces all prior versions. + * + * Copyright (c) 2013-2021, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, + * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + #include #include diff --git a/spine-sdl/src/spine-sdl-c.c b/spine-sdl/src/spine-sdl-c.c index af3cf3875..fb485d6a5 100644 --- a/spine-sdl/src/spine-sdl-c.c +++ b/spine-sdl/src/spine-sdl-c.c @@ -36,7 +36,10 @@ #include +_SP_ARRAY_IMPLEMENT_TYPE_NO_CONTAINS(spSdlVertexArray, SDL_Vertex) + spSkeletonDrawable *spSkeletonDrawable_create(spSkeletonData *skeletonData, spAnimationStateData *animationStateData) { + spBone_setYDown(-1); spSkeletonDrawable *self = NEW(spSkeletonDrawable); self->skeleton = spSkeleton_create(skeletonData); self->animationState = spAnimationState_create(animationStateData); @@ -69,7 +72,7 @@ void spSkeletonDrawable_draw(spSkeletonDrawable *self, struct SDL_Renderer *rend spSkeletonClipping *clipper = self->clipper; SDL_Texture *texture; SDL_Vertex sdlVertex; - for (unsigned i = 0; i < skeleton->slotsCount; ++i) { + for (int i = 0; i < skeleton->slotsCount; ++i) { spSlot *slot = skeleton->drawOrder[i]; spAttachment *attachment = slot->attachment; if (!attachment) continue; @@ -100,8 +103,8 @@ void spSkeletonDrawable_draw(spSkeletonDrawable *self, struct SDL_Renderer *rend spFloatArray_setSize(vertices, 8); spRegionAttachment_computeWorldVertices(region, slot, vertices->items, 0, 2); verticesCount = 4; - uvs = ®ion->uvs; - indices = &quadIndices; + uvs = region->uvs; + indices = quadIndices; indicesCount = 6; texture = (SDL_Texture *) ((spAtlasRegion*)region->rendererObject)->page->rendererObject; } else if (attachment->type == SP_ATTACHMENT_MESH) { @@ -115,7 +118,7 @@ void spSkeletonDrawable_draw(spSkeletonDrawable *self, struct SDL_Renderer *rend } spFloatArray_setSize(vertices, mesh->super.worldVerticesLength); - spVertexAttachment_computeWorldVertices(SUPER(mesh), slot, 0, mesh->super.worldVerticesLength, vertices, 0, 2); + spVertexAttachment_computeWorldVertices(SUPER(mesh), slot, 0, mesh->super.worldVerticesLength, vertices->items, 0, 2); verticesCount = mesh->super.worldVerticesLength >> 1; uvs = mesh->uvs; indices = mesh->triangles; @@ -138,8 +141,8 @@ void spSkeletonDrawable_draw(spSkeletonDrawable *self, struct SDL_Renderer *rend sdlVertex.color.a = a; if (spSkeletonClipping_isClipping(clipper)) { - spSkeletonClipping_clipTriangles(clipper, vertices, verticesCount << 1, indices, indicesCount, uvs, 2); - vertices = clipper->clippedVertices->items; + spSkeletonClipping_clipTriangles(clipper, vertices->items, verticesCount << 1, indices, indicesCount, uvs, 2); + vertices = clipper->clippedVertices; verticesCount = clipper->clippedVertices->size >> 1; uvs = clipper->clippedUVs->items; indices = clipper->clippedTriangles->items; @@ -184,7 +187,7 @@ void _spAtlasPage_createTexture(spAtlasPage *self, const char *path) { int width, height, components; stbi_uc *imageData = stbi_load(path, &width, &height, &components, 4); if (!imageData) return; - SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, + SDL_Texture *texture = SDL_CreateTexture((SDL_Renderer*)self->atlas->rendererObject, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, height); if (!texture) { stbi_image_free(imageData); @@ -195,6 +198,7 @@ void _spAtlasPage_createTexture(spAtlasPage *self, const char *path) { return; } stbi_image_free(imageData); + self->rendererObject = texture; return; } @@ -204,4 +208,4 @@ void _spAtlasPage_disposeTexture(spAtlasPage *self) { char *_spUtil_readFile(const char *path, int *length) { return _spReadFile(path, length); -} \ No newline at end of file +} diff --git a/spine-sdl/src/spine-sdl-c.h b/spine-sdl/src/spine-sdl-c.h index 77c30ecf4..4b3976c1c 100644 --- a/spine-sdl/src/spine-sdl-c.h +++ b/spine-sdl/src/spine-sdl-c.h @@ -39,7 +39,7 @@ extern "C" { struct SDL_Renderer; struct SDL_Texture; struct SDL_Vertex; -_SP_ARRAY_DECLARE_TYPE(spSdlVertexArray, struct SDL_Vertex); +_SP_ARRAY_DECLARE_TYPE(spSdlVertexArray, struct SDL_Vertex) typedef struct spSkeletonDrawable { spSkeleton *skeleton; diff --git a/spine-sdl/src/stb_image.h b/spine-sdl/src/stb_image.h index c97eea70e..295bdfaac 100644 --- a/spine-sdl/src/stb_image.h +++ b/spine-sdl/src/stb_image.h @@ -7976,4 +7976,4 @@ 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. ------------------------------------------------------------------------------ -*/ \ No newline at end of file +*/ From f3e03467b5bcd485ad86f25b5230c69d12687681 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Tue, 2 Aug 2022 10:46:28 +0200 Subject: [PATCH 08/18] [sdl] Formatting pass. --- spine-sdl/example/main.c | 93 +++++------ spine-sdl/src/spine-sdl-c.c | 278 ++++++++++++++++---------------- spine-sdl/src/spine-sdl-c.h | 12 +- spine-sdl/src/spine-sdl-cpp.cpp | 14 +- 4 files changed, 199 insertions(+), 198 deletions(-) diff --git a/spine-sdl/example/main.c b/spine-sdl/example/main.c index 07cafe8f2..0dcf11ca5 100644 --- a/spine-sdl/example/main.c +++ b/spine-sdl/example/main.c @@ -31,58 +31,59 @@ #include int main() { - if (SDL_Init(SDL_INIT_VIDEO)) { - printf("Error: %s", SDL_GetError()); - return -1; - } - SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, 0); - if (!window) { - printf("Error: %s", SDL_GetError()); - return -1; - } - SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (!renderer) { - printf("Error: %s", SDL_GetError()); - return -1; - } + if (SDL_Init(SDL_INIT_VIDEO)) { + printf("Error: %s", SDL_GetError()); + return -1; + } + SDL_Window *window = SDL_CreateWindow("Spine SDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 800, 600, 0); + if (!window) { + printf("Error: %s", SDL_GetError()); + return -1; + } + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + if (!renderer) { + printf("Error: %s", SDL_GetError()); + return -1; + } - spAtlas *atlas = spAtlas_createFromFile("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas", renderer); - spSkeletonJson *json = spSkeletonJson_create(atlas); - json->scale = 0.5f; - spSkeletonData *skeletonData = spSkeletonJson_readSkeletonDataFile(json, "/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.json"); - spAnimationStateData *animationStateData = spAnimationStateData_create(skeletonData); - spSkeletonDrawable *drawable = spSkeletonDrawable_create(skeletonData, animationStateData); + spAtlas *atlas = spAtlas_createFromFile("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas", renderer); + spSkeletonJson *json = spSkeletonJson_create(atlas); + json->scale = 0.5f; + spSkeletonData *skeletonData = spSkeletonJson_readSkeletonDataFile(json, "/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.json"); + spAnimationStateData *animationStateData = spAnimationStateData_create(skeletonData); + spSkeletonDrawable *drawable = spSkeletonDrawable_create(skeletonData, animationStateData); - drawable->skeleton->x = 400; drawable->skeleton->y = 500; - spSkeleton_setToSetupPose(drawable->skeleton); - spSkeletonDrawable_update(drawable, 0); - spAnimationState_setAnimationByName(drawable->animationState, 0, "run", -1); + drawable->skeleton->x = 400; + drawable->skeleton->y = 500; + spSkeleton_setToSetupPose(drawable->skeleton); + spSkeletonDrawable_update(drawable, 0); + spAnimationState_setAnimationByName(drawable->animationState, 0, "run", -1); - int quit = 0; - uint64_t lastFrameTime = SDL_GetPerformanceCounter(); - while (!quit) { - SDL_Event event; - while (SDL_PollEvent(&event) != 0) { - if (event.type == SDL_QUIT) { - quit = -1; - break; - } - } + int quit = 0; + uint64_t lastFrameTime = SDL_GetPerformanceCounter(); + while (!quit) { + SDL_Event event; + while (SDL_PollEvent(&event) != 0) { + if (event.type == SDL_QUIT) { + quit = -1; + break; + } + } - SDL_SetRenderDrawColor(renderer, 94, 93, 96, 255); - SDL_RenderClear(renderer); + SDL_SetRenderDrawColor(renderer, 94, 93, 96, 255); + SDL_RenderClear(renderer); - uint64_t now = SDL_GetPerformanceCounter(); - double deltaTime = (now - lastFrameTime) / (double) SDL_GetPerformanceFrequency(); - lastFrameTime = now; + uint64_t now = SDL_GetPerformanceCounter(); + double deltaTime = (now - lastFrameTime) / (double) SDL_GetPerformanceFrequency(); + lastFrameTime = now; - spSkeletonDrawable_update(drawable, deltaTime); - spSkeletonDrawable_draw(drawable, renderer); + spSkeletonDrawable_update(drawable, deltaTime); + spSkeletonDrawable_draw(drawable, renderer); - SDL_RenderPresent(renderer); - } + SDL_RenderPresent(renderer); + } - SDL_DestroyWindow(window); - SDL_Quit(); - return 0; + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; } diff --git a/spine-sdl/src/spine-sdl-c.c b/spine-sdl/src/spine-sdl-c.c index fb485d6a5..4f99357bb 100644 --- a/spine-sdl/src/spine-sdl-c.c +++ b/spine-sdl/src/spine-sdl-c.c @@ -39,173 +39,173 @@ _SP_ARRAY_IMPLEMENT_TYPE_NO_CONTAINS(spSdlVertexArray, SDL_Vertex) spSkeletonDrawable *spSkeletonDrawable_create(spSkeletonData *skeletonData, spAnimationStateData *animationStateData) { - spBone_setYDown(-1); - spSkeletonDrawable *self = NEW(spSkeletonDrawable); - self->skeleton = spSkeleton_create(skeletonData); - self->animationState = spAnimationState_create(animationStateData); - self->sdlIndices = spIntArray_create(12); - self->sdlVertices = spSdlVertexArray_create(12); - self->worldVertices = spFloatArray_create(12); - self->clipper = spSkeletonClipping_create(); - return self; + spBone_setYDown(-1); + spSkeletonDrawable *self = NEW(spSkeletonDrawable); + self->skeleton = spSkeleton_create(skeletonData); + self->animationState = spAnimationState_create(animationStateData); + self->sdlIndices = spIntArray_create(12); + self->sdlVertices = spSdlVertexArray_create(12); + self->worldVertices = spFloatArray_create(12); + self->clipper = spSkeletonClipping_create(); + return self; } void spSkeletonDrawable_dispose(spSkeletonDrawable *self) { - spSkeleton_dispose(self->skeleton); - spAnimationState_dispose(self->animationState); - spIntArray_dispose(self->sdlIndices); - spSdlVertexArray_dispose(self->sdlVertices); - spFloatArray_dispose(self->worldVertices); - spSkeletonClipping_dispose(self->clipper); - FREE(self); + spSkeleton_dispose(self->skeleton); + spAnimationState_dispose(self->animationState); + spIntArray_dispose(self->sdlIndices); + spSdlVertexArray_dispose(self->sdlVertices); + spFloatArray_dispose(self->worldVertices); + spSkeletonClipping_dispose(self->clipper); + FREE(self); } void spSkeletonDrawable_update(spSkeletonDrawable *self, float delta) { - spAnimationState_update(self->animationState, delta); - spAnimationState_apply(self->animationState, self->skeleton); - spSkeleton_updateWorldTransform(self->skeleton); + spAnimationState_update(self->animationState, delta); + spAnimationState_apply(self->animationState, self->skeleton); + spSkeleton_updateWorldTransform(self->skeleton); } void spSkeletonDrawable_draw(spSkeletonDrawable *self, struct SDL_Renderer *renderer) { - static unsigned short quadIndices[] = { 0, 1, 2, 2, 3, 0 }; - spSkeleton *skeleton = self->skeleton; - spSkeletonClipping *clipper = self->clipper; - SDL_Texture *texture; - SDL_Vertex sdlVertex; - for (int i = 0; i < skeleton->slotsCount; ++i) { - spSlot *slot = skeleton->drawOrder[i]; - spAttachment *attachment = slot->attachment; - if (!attachment) continue; + static unsigned short quadIndices[] = {0, 1, 2, 2, 3, 0}; + spSkeleton *skeleton = self->skeleton; + spSkeletonClipping *clipper = self->clipper; + SDL_Texture *texture; + SDL_Vertex sdlVertex; + for (int i = 0; i < skeleton->slotsCount; ++i) { + spSlot *slot = skeleton->drawOrder[i]; + spAttachment *attachment = slot->attachment; + if (!attachment) continue; - // Early out if the slot color is 0 or the bone is not active - if (slot->color.a == 0 || !slot->bone->active) { - spSkeletonClipping_clipEnd(clipper, slot); - continue; - } + // Early out if the slot color is 0 or the bone is not active + if (slot->color.a == 0 || !slot->bone->active) { + spSkeletonClipping_clipEnd(clipper, slot); + continue; + } - spFloatArray *vertices = self->worldVertices; - int verticesCount = 0; - float *uvs = NULL; - unsigned short *indices; - int indicesCount = 0; - spColor *attachmentColor = NULL; + spFloatArray *vertices = self->worldVertices; + int verticesCount = 0; + float *uvs = NULL; + unsigned short *indices; + int indicesCount = 0; + spColor *attachmentColor = NULL; - if (attachment->type == SP_ATTACHMENT_REGION) { - spRegionAttachment *region = (spRegionAttachment *) attachment; - attachmentColor = ®ion->color; + if (attachment->type == SP_ATTACHMENT_REGION) { + spRegionAttachment *region = (spRegionAttachment *) attachment; + attachmentColor = ®ion->color; - // Early out if the slot color is 0 - if (attachmentColor->a == 0) { - spSkeletonClipping_clipEnd(clipper, slot); - continue; - } + // Early out if the slot color is 0 + if (attachmentColor->a == 0) { + spSkeletonClipping_clipEnd(clipper, slot); + continue; + } - spFloatArray_setSize(vertices, 8); - spRegionAttachment_computeWorldVertices(region, slot, vertices->items, 0, 2); - verticesCount = 4; - uvs = region->uvs; - indices = quadIndices; - indicesCount = 6; - texture = (SDL_Texture *) ((spAtlasRegion*)region->rendererObject)->page->rendererObject; - } else if (attachment->type == SP_ATTACHMENT_MESH) { - spMeshAttachment *mesh = (spMeshAttachment *) attachment; - attachmentColor = &mesh->color; + spFloatArray_setSize(vertices, 8); + spRegionAttachment_computeWorldVertices(region, slot, vertices->items, 0, 2); + verticesCount = 4; + uvs = region->uvs; + indices = quadIndices; + indicesCount = 6; + texture = (SDL_Texture *) ((spAtlasRegion *) region->rendererObject)->page->rendererObject; + } else if (attachment->type == SP_ATTACHMENT_MESH) { + spMeshAttachment *mesh = (spMeshAttachment *) attachment; + attachmentColor = &mesh->color; - // Early out if the slot color is 0 - if (attachmentColor->a == 0) { - spSkeletonClipping_clipEnd(clipper, slot); - continue; - } + // Early out if the slot color is 0 + if (attachmentColor->a == 0) { + spSkeletonClipping_clipEnd(clipper, slot); + continue; + } - spFloatArray_setSize(vertices, mesh->super.worldVerticesLength); - spVertexAttachment_computeWorldVertices(SUPER(mesh), slot, 0, mesh->super.worldVerticesLength, vertices->items, 0, 2); - verticesCount = mesh->super.worldVerticesLength >> 1; - uvs = mesh->uvs; - indices = mesh->triangles; - indicesCount = mesh->trianglesCount; - texture = (SDL_Texture *) ((spAtlasRegion*)mesh->rendererObject)->page->rendererObject; - } else if (attachment->type == SP_ATTACHMENT_CLIPPING) { - spClippingAttachment *clip = (spClippingAttachment *) slot->attachment; - spSkeletonClipping_clipStart(clipper, slot, clip); - continue; - } else - continue; + spFloatArray_setSize(vertices, mesh->super.worldVerticesLength); + spVertexAttachment_computeWorldVertices(SUPER(mesh), slot, 0, mesh->super.worldVerticesLength, vertices->items, 0, 2); + verticesCount = mesh->super.worldVerticesLength >> 1; + uvs = mesh->uvs; + indices = mesh->triangles; + indicesCount = mesh->trianglesCount; + texture = (SDL_Texture *) ((spAtlasRegion *) mesh->rendererObject)->page->rendererObject; + } else if (attachment->type == SP_ATTACHMENT_CLIPPING) { + spClippingAttachment *clip = (spClippingAttachment *) slot->attachment; + spSkeletonClipping_clipStart(clipper, slot, clip); + continue; + } else + continue; - Uint8 r = (Uint8)(skeleton->color.r * slot->color.r * attachmentColor->r * 255); - Uint8 g = (Uint8)(skeleton->color.g * slot->color.g * attachmentColor->g * 255); - Uint8 b = (Uint8)(skeleton->color.b * slot->color.b * attachmentColor->b * 255); - Uint8 a = (Uint8)(skeleton->color.a * slot->color.a * attachmentColor->a * 255); - sdlVertex.color.r = r; - sdlVertex.color.g = g; - sdlVertex.color.b = b; - sdlVertex.color.a = a; + Uint8 r = (Uint8) (skeleton->color.r * slot->color.r * attachmentColor->r * 255); + Uint8 g = (Uint8) (skeleton->color.g * slot->color.g * attachmentColor->g * 255); + Uint8 b = (Uint8) (skeleton->color.b * slot->color.b * attachmentColor->b * 255); + Uint8 a = (Uint8) (skeleton->color.a * slot->color.a * attachmentColor->a * 255); + sdlVertex.color.r = r; + sdlVertex.color.g = g; + sdlVertex.color.b = b; + sdlVertex.color.a = a; - if (spSkeletonClipping_isClipping(clipper)) { - spSkeletonClipping_clipTriangles(clipper, vertices->items, verticesCount << 1, indices, indicesCount, uvs, 2); - vertices = clipper->clippedVertices; - verticesCount = clipper->clippedVertices->size >> 1; - uvs = clipper->clippedUVs->items; - indices = clipper->clippedTriangles->items; - indicesCount = clipper->clippedTriangles->size; - } + if (spSkeletonClipping_isClipping(clipper)) { + spSkeletonClipping_clipTriangles(clipper, vertices->items, verticesCount << 1, indices, indicesCount, uvs, 2); + vertices = clipper->clippedVertices; + verticesCount = clipper->clippedVertices->size >> 1; + uvs = clipper->clippedUVs->items; + indices = clipper->clippedTriangles->items; + indicesCount = clipper->clippedTriangles->size; + } - spSdlVertexArray_clear(self->sdlVertices); - for (int ii = 0; ii < verticesCount << 1; ii += 2) { - sdlVertex.position.x = vertices->items[ii]; - sdlVertex.position.y = vertices->items[ii + 1]; - sdlVertex.tex_coord.x = uvs[ii]; - sdlVertex.tex_coord.y = uvs[ii + 1]; - spSdlVertexArray_add(self->sdlVertices, sdlVertex); - } - spIntArray_clear(self->sdlIndices); - for (int ii = 0; ii < (int) indicesCount; ii++) - spIntArray_add(self->sdlIndices, indices[ii]); + spSdlVertexArray_clear(self->sdlVertices); + for (int ii = 0; ii < verticesCount << 1; ii += 2) { + sdlVertex.position.x = vertices->items[ii]; + sdlVertex.position.y = vertices->items[ii + 1]; + sdlVertex.tex_coord.x = uvs[ii]; + sdlVertex.tex_coord.y = uvs[ii + 1]; + spSdlVertexArray_add(self->sdlVertices, sdlVertex); + } + spIntArray_clear(self->sdlIndices); + for (int ii = 0; ii < (int) indicesCount; ii++) + spIntArray_add(self->sdlIndices, indices[ii]); - switch (slot->data->blendMode) { - case SP_BLEND_MODE_NORMAL: - SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); - break; - case SP_BLEND_MODE_MULTIPLY: - SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD); - break; - case SP_BLEND_MODE_ADDITIVE: - SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD); - break; - case SP_BLEND_MODE_SCREEN: - SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); - break; - } + switch (slot->data->blendMode) { + case SP_BLEND_MODE_NORMAL: + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + break; + case SP_BLEND_MODE_MULTIPLY: + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MOD); + break; + case SP_BLEND_MODE_ADDITIVE: + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD); + break; + case SP_BLEND_MODE_SCREEN: + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); + break; + } - SDL_RenderGeometry(renderer, texture, self->sdlVertices->items, self->sdlVertices->size, self->sdlIndices->items, - indicesCount); - spSkeletonClipping_clipEnd(clipper, slot); - } - spSkeletonClipping_clipEnd2(clipper); + SDL_RenderGeometry(renderer, texture, self->sdlVertices->items, self->sdlVertices->size, self->sdlIndices->items, + indicesCount); + spSkeletonClipping_clipEnd(clipper, slot); + } + spSkeletonClipping_clipEnd2(clipper); } void _spAtlasPage_createTexture(spAtlasPage *self, const char *path) { - int width, height, components; - stbi_uc *imageData = stbi_load(path, &width, &height, &components, 4); - if (!imageData) return; - SDL_Texture *texture = SDL_CreateTexture((SDL_Renderer*)self->atlas->rendererObject, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, - height); - if (!texture) { - stbi_image_free(imageData); - return; - } - if (SDL_UpdateTexture(texture, NULL, imageData, width * 4)) { - stbi_image_free(imageData); - return; - } - stbi_image_free(imageData); - self->rendererObject = texture; - return; + int width, height, components; + stbi_uc *imageData = stbi_load(path, &width, &height, &components, 4); + if (!imageData) return; + SDL_Texture *texture = SDL_CreateTexture((SDL_Renderer *) self->atlas->rendererObject, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STATIC, width, + height); + if (!texture) { + stbi_image_free(imageData); + return; + } + if (SDL_UpdateTexture(texture, NULL, imageData, width * 4)) { + stbi_image_free(imageData); + return; + } + stbi_image_free(imageData); + self->rendererObject = texture; + return; } void _spAtlasPage_disposeTexture(spAtlasPage *self) { - SDL_DestroyTexture((SDL_Texture *) self->rendererObject); + SDL_DestroyTexture((SDL_Texture *) self->rendererObject); } char *_spUtil_readFile(const char *path, int *length) { - return _spReadFile(path, length); + return _spReadFile(path, length); } diff --git a/spine-sdl/src/spine-sdl-c.h b/spine-sdl/src/spine-sdl-c.h index 4b3976c1c..b8cbf2b63 100644 --- a/spine-sdl/src/spine-sdl-c.h +++ b/spine-sdl/src/spine-sdl-c.h @@ -42,13 +42,13 @@ struct SDL_Vertex; _SP_ARRAY_DECLARE_TYPE(spSdlVertexArray, struct SDL_Vertex) typedef struct spSkeletonDrawable { - spSkeleton *skeleton; - spAnimationState *animationState; + spSkeleton *skeleton; + spAnimationState *animationState; - spSkeletonClipping *clipper; - spFloatArray *worldVertices; - spSdlVertexArray *sdlVertices; - spIntArray *sdlIndices; + spSkeletonClipping *clipper; + spFloatArray *worldVertices; + spSdlVertexArray *sdlVertices; + spIntArray *sdlIndices; } spSkeletonDrawable; SP_API spSkeletonDrawable *spSkeletonDrawable_create(spSkeletonData *skeletonData, spAnimationStateData *animationStateData); diff --git a/spine-sdl/src/spine-sdl-cpp.cpp b/spine-sdl/src/spine-sdl-cpp.cpp index 321aad8af..365abf221 100644 --- a/spine-sdl/src/spine-sdl-cpp.cpp +++ b/spine-sdl/src/spine-sdl-cpp.cpp @@ -58,13 +58,13 @@ void SkeletonDrawable::update(float delta) { } void SkeletonDrawable::draw(SDL_Renderer *renderer) { - Vector quadIndices; - quadIndices.add(0); - quadIndices.add(1); - quadIndices.add(2); - quadIndices.add(2); - quadIndices.add(3); - quadIndices.add(0); + Vector quadIndices; + quadIndices.add(0); + quadIndices.add(1); + quadIndices.add(2); + quadIndices.add(2); + quadIndices.add(3); + quadIndices.add(0); SDL_Texture *texture; SDL_Vertex sdlVertex; for (unsigned i = 0; i < skeleton->getSlots().size(); ++i) { From 787af434ff727de26a6fd54280bbc5501160f877 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Tue, 2 Aug 2022 11:45:24 +0200 Subject: [PATCH 09/18] [sdl] Updated README, copying example files, fix gnu-sed macOS issue --- examples/export/runtimes.sh | 10 +- spine-sdl/CMakeLists.txt | 9 + spine-sdl/README.md | 64 + spine-sdl/data/spineboy-pro.json | 8707 ++++++++++++++++++++++++++++++ spine-sdl/data/spineboy.atlas | 101 + spine-sdl/data/spineboy.png | Bin 0 -> 243396 bytes spine-sdl/example/main.c | 9 +- spine-sdl/example/main.cpp | 8 +- 8 files changed, 8899 insertions(+), 9 deletions(-) create mode 100644 spine-sdl/README.md create mode 100644 spine-sdl/data/spineboy-pro.json create mode 100644 spine-sdl/data/spineboy.atlas create mode 100644 spine-sdl/data/spineboy.png diff --git a/examples/export/runtimes.sh b/examples/export/runtimes.sh index 5c514bfe7..ec1ba154b 100755 --- a/examples/export/runtimes.sh +++ b/examples/export/runtimes.sh @@ -155,6 +155,12 @@ cp -f ../../spine-unity/Assets/Spine\ Examples/Spine\ Skeletons/Raggedy\ Spinebo cp -f ../../spine-unity/Assets/Spine\ Examples/Spine\ Skeletons/Raggedy\ Spineboy/Raggedy\ Spineboy.atlas.txt "$ROOT/spine-godot/example-v4/assets/raggedyspineboy/Raggedy Spineboy.atlas" cp -f ../../spine-unity/Assets/Spine\ Examples/Spine\ Skeletons/Raggedy\ Spineboy/Raggedy\ Spineboy.png "$ROOT/spine-godot/example-v4/assets/raggedyspineboy/" +echo "spine-sdl" +rm -f "$ROOT/spine-sdl/data/"* +cp -f ../spineboy/export/spineboy-pro.json "$ROOT/spine-sdl/data/" +cp -f ../spineboy/export/spineboy.atlas "$ROOT/spine-sdl/data/" +cp -f ../spineboy/export/spineboy.png "$ROOT/spine-sdl/data/" + echo "spine-sfml-c" rm "$ROOT/spine-sfml/c/data/"* cp -f ../coin/export/coin-pro.json "$ROOT/spine-sfml/c/data/" @@ -382,11 +388,11 @@ UNITY_SOURCE_DIR=../spine-unity # and error out otherwise. sed="sed" if [[ $OSTYPE == 'darwin'* ]]; then - if [ ! -f "/usr/local/bin/gsed" ]; then + if [ ! -f "/opt/homebrew/bin/gsed" ]; then echo "macOS sed detected. Please install GNU sed via brew install gnu-sed" exit -1 fi - sed="/usr/local/bin/gsed" + sed="/opt/homebrew/bin/gsed" fi # Do not delete everything in unity dirs, especially not .meta files. diff --git a/spine-sdl/CMakeLists.txt b/spine-sdl/CMakeLists.txt index 85cfae127..e0109f838 100644 --- a/spine-sdl/CMakeLists.txt +++ b/spine-sdl/CMakeLists.txt @@ -30,3 +30,12 @@ target_link_libraries(spine-sdl-c-example SDL2 spine-sdl-c) add_executable(spine-sdl-cpp-example example/main.cpp) target_link_libraries(spine-sdl-cpp-example SDL2 spine-sdl-cpp) + +# copy data to build directory +add_custom_command(TARGET spine-sdl-c-example PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_LIST_DIR}/data $/data) + +add_custom_command(TARGET spine-sdl-cpp-example PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_LIST_DIR}/data $/data) diff --git a/spine-sdl/README.md b/spine-sdl/README.md new file mode 100644 index 000000000..7ee7a30c1 --- /dev/null +++ b/spine-sdl/README.md @@ -0,0 +1,64 @@ +# spine-sdl + +The spine-sdl runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [SDL](https://www.libsdl.org/). spine-sdl is based on [spine-c](../../spine-c) and [spine-cpp](../spine-cpp), depending on whether you want to use a C or C++ implementation. + +## Licensing + +You are welcome to evaluate the Spine Runtimes and the examples we provide in this repository free of charge. + +You can integrate the Spine Runtimes into your software free of charge, but users of your software must have their own [Spine license](https://esotericsoftware.com/spine-purchase). Please make your users aware of this requirement! This option is often chosen by those making development tools, such as an SDK, game toolkit, or software library. + +In order to distribute your software containing the Spine Runtimes to others that don't have a Spine license, you need a [Spine license](https://esotericsoftware.com/spine-purchase) at the time of integration. Then you can distribute your software containing the Spine Runtimes however you like, provided others don't modify it or use it to create new software. If others want to do that, they'll need their own Spine license. + +For the official legal terms governing the Spine Runtimes, please read the [Spine Runtimes License Agreement](http://esotericsoftware.com/spine-runtimes-license) and Section 2 of the [Spine Editor License Agreement](http://esotericsoftware.com/spine-editor-license#s2). + +## Spine version + +spine-sdl works with data exported from Spine 4.1.xx. + +spine-sdl supports all Spine features except premultiplied alpha, screen blend mode, and two color tinting. + +## Usage +1. Create a new SDL project. See the [SDL documentation](https://wiki.libsdl.org/FrontPage) or have a look at the example in this repository. +2. Download the Spine Runtimes source using git (`git clone https://github.com/esotericsoftware/spine-runtimes`) or download it as a zip via the download button above. +3. If you are using C, add the sources from `spine-c/spine-c/src/spine` and `spine-sdl/src/spine-sdl-c.c` to your project, and add the folder `spine-c/spine-c/include` and `spine-sdl/src/spine-sdl-c.h` to your header search path. Note that includes are specified as `#inclue `, so the `spine` directory cannot be omitted when copying the source files. +3. If you are using C++, add the sources from `spine-cpp/spine-cpp/src/spine` and `spine-sdl/src/spine-sdl-cpp.cpp` to your project, and add the folder `spine-cpp/spine-cpp/include` and `spine-sdl/src/spine-sdl-cpp.h` to your header search path. Note that includes are specified as `#inclue `, so the `spine` directory cannot be omitted when copying the source files. + +See the [Spine Runtimes documentation](http://esotericsoftware.com/spine-documentation#runtimesTitle) on how to use the APIs or check out the Spine SDL example in this repository. + +## Example +The Spine SFML example works on Windows, Linux and Mac OS X. For a spine-c based example, see [example/main.c](example/main.c), for a spine-cpp example see [example/main.cpp](example/main.cpp). + +### Windows +1. Install [Visual Studio Community](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspx). Make sure you install support for C++ as well as th Windows SDK for XP/7/8. +2. Install CMake via the [Windows installer package](https://cmake.org/download/). +3. Download the Spine Runtimes repository using git (`git clone https://github.com/esotericsoftware/spine-runtimes`) or download it as a zip via the download button above. +4. Run CMake GUI from the start menu +5. Click `Browse Source` and select the directory `spine-runtimes` +6. Click `Browse Build` and select the `spine-runtimes/spine-sdl/build` directory. You can create the `build` folder directly in the file dialog via `New Folder`. +7. Click `Configure`. Then click `Generate`. This will create a Visual Studio solution file called `spine.sln` in `spine-runtimes/spine-sdl/build` and also download the SDL dependencies. +8. Open the `spine.sln` file in Visual Studio +9. Right click the `spine-sfml-example` project in the solution explorer and select `Set as Startup Project` from the context menu +10. Right click the `spine-sfml-example` project in the solution explorer and select `Properties` from the context menu +11. Select `Debugging` in the left-hand list, then set `Working Directory` to `$(OutputPath)` +12. Click `Local Windows Debugger` to run the example + +The entire example code is contained in [main.cpp](example/main.cpp#L61) + +### Linux +1. Install the [SDL build dependencies](https://github.com/libsdl-org/SDL/blob/main/docs/README-linux.md) +3. Download the Spine Runtimes repository using git (`git clone https://github.com/esotericsoftware/spine-runtimes`) or download it as a zip via the download button above. +4. Open a terminal, and `cd` into the `spine-runtimes/spine-sdl` folder +5. Type `mkdir build && cd build && cmake ../..` to generate Make files +6. Type `make` to compile the example +7. Run the example by `cd spine-sdl && ./spine-sdl-c-example` (C) or by `cd spine-sdl && ./spine-sdl-cpp-example` (C++) + +### Mac OS X +1. Install [Xcode](https://developer.apple.com/xcode/) +2. Install [Homebrew](http://brew.sh/) +3. Open a terminal and install CMake via `brew install cmake` +3. Download the Spine Runtimes repository using git (`git clone https://github.com/esotericsoftware/spine-runtimes`) or download it as a zip via the download button above. +4. Open a terminal, and `cd` into the `spine-runtimes/spine-sdl` folder +5. Type `mkdir build && cd build && cmake ../..` to generate Make files +6. Type `make` to compile the example +7. Run the example by `cd spine-sdl && ./spine-sdl-c-example` (C) or by `cd spine-sdl && ./spine-sdl-cpp-example` (C++) diff --git a/spine-sdl/data/spineboy-pro.json b/spine-sdl/data/spineboy-pro.json new file mode 100644 index 000000000..b4dd8b40f --- /dev/null +++ b/spine-sdl/data/spineboy-pro.json @@ -0,0 +1,8707 @@ +{ +"skeleton": { + "hash": "itfFESDjM1c", + "spine": "4.1.23-beta", + "x": -188.63, + "y": -7.94, + "width": 418.45, + "height": 686.2, + "images": "./images/", + "audio": "" +}, +"bones": [ + { "name": "root", "rotation": 0.05 }, + { "name": "hip", "parent": "root", "y": 247.27 }, + { "name": "crosshair", "parent": "root", "x": 302.83, "y": 569.45, "color": "ff3f00ff" }, + { + "name": "aim-constraint-target", + "parent": "hip", + "length": 26.24, + "rotation": 19.61, + "x": 1.02, + "y": 5.62, + "color": "abe323ff" + }, + { "name": "rear-foot-target", "parent": "root", "x": 61.91, "y": 0.42, "color": "ff3f00ff" }, + { "name": "rear-leg-target", "parent": "rear-foot-target", "x": -33.91, "y": 37.34, "color": "ff3f00ff" }, + { + "name": "rear-thigh", + "parent": "hip", + "length": 85.72, + "rotation": -72.54, + "x": 8.91, + "y": -5.63, + "color": "ff000dff" + }, + { + "name": "rear-shin", + "parent": "rear-thigh", + "length": 121.88, + "rotation": -19.83, + "x": 86.1, + "y": -1.33, + "color": "ff000dff" + }, + { + "name": "rear-foot", + "parent": "rear-shin", + "length": 51.58, + "rotation": 45.78, + "x": 121.46, + "y": -0.76, + "color": "ff000dff" + }, + { + "name": "back-foot-tip", + "parent": "rear-foot", + "length": 50.3, + "rotation": -0.85, + "x": 51.17, + "y": 0.24, + "transform": "noRotationOrReflection", + "color": "ff000dff" + }, + { "name": "board-ik", "parent": "root", "x": -131.78, "y": 69.09, "color": "4c56ffff" }, + { "name": "clipping", "parent": "root" }, + { "name": "hoverboard-controller", "parent": "root", "rotation": -0.28, "x": -329.69, "y": 69.82, "color": "ff0004ff" }, + { "name": "exhaust1", "parent": "hoverboard-controller", "rotation": 3.02, "x": -249.68, "y": 53.39 }, + { "name": "exhaust2", "parent": "hoverboard-controller", "rotation": 26.34, "x": -191.6, "y": -22.92 }, + { + "name": "exhaust3", + "parent": "hoverboard-controller", + "rotation": -12.34, + "x": -236.03, + "y": 80.54, + "scaleX": 0.7847, + "scaleY": 0.7847 + }, + { "name": "portal-root", "parent": "root", "x": 12.9, "y": 328.54, "scaleX": 2.0334, "scaleY": 2.0334 }, + { "name": "flare1", "parent": "portal-root", "x": -6.34, "y": -161.57 }, + { "name": "flare10", "parent": "portal-root", "x": -6.34, "y": -161.57 }, + { "name": "flare2", "parent": "portal-root", "x": -6.34, "y": -161.57 }, + { "name": "flare3", "parent": "portal-root", "x": -6.34, "y": -161.57 }, + { "name": "flare4", "parent": "portal-root", "x": -6.34, "y": -161.57 }, + { "name": "flare5", "parent": "portal-root", "x": -6.34, "y": -161.57 }, + { "name": "flare6", "parent": "portal-root", "x": -6.34, "y": -161.57 }, + { "name": "flare7", "parent": "portal-root", "x": -6.34, "y": -161.57 }, + { "name": "flare8", "parent": "portal-root", "x": -6.34, "y": -161.57 }, + { "name": "flare9", "parent": "portal-root", "x": -6.34, "y": -161.57 }, + { + "name": "torso", + "parent": "hip", + "length": 42.52, + "rotation": 103.82, + "x": -1.62, + "y": 4.9, + "color": "e0da19ff" + }, + { "name": "torso2", "parent": "torso", "length": 42.52, "x": 42.52, "color": "e0da19ff" }, + { "name": "torso3", "parent": "torso2", "length": 42.52, "x": 42.52, "color": "e0da19ff" }, + { "name": "front-shoulder", "parent": "torso3", "rotation": 255.89, "x": 18.72, "y": 19.33, "color": "00ff04ff" }, + { "name": "front-upper-arm", "parent": "front-shoulder", "length": 69.45, "rotation": -87.51, "color": "00ff04ff" }, + { + "name": "front-bracer", + "parent": "front-upper-arm", + "length": 40.57, + "rotation": 18.3, + "x": 68.8, + "y": -0.68, + "color": "00ff04ff" + }, + { + "name": "front-fist", + "parent": "front-bracer", + "length": 65.39, + "rotation": 12.43, + "x": 40.57, + "y": 0.2, + "color": "00ff04ff" + }, + { "name": "front-foot-target", "parent": "root", "x": -13.53, "y": 0.04, "color": "ff3f00ff" }, + { "name": "front-leg-target", "parent": "front-foot-target", "x": -28.4, "y": 29.06, "color": "ff3f00ff" }, + { + "name": "front-thigh", + "parent": "hip", + "length": 74.81, + "rotation": -95.51, + "x": -17.46, + "y": -11.64, + "color": "00ff04ff" + }, + { + "name": "front-shin", + "parent": "front-thigh", + "length": 128.77, + "rotation": -2.21, + "x": 78.69, + "y": 1.6, + "color": "00ff04ff" + }, + { + "name": "front-foot", + "parent": "front-shin", + "length": 41.01, + "rotation": 51.27, + "x": 128.76, + "y": -0.34, + "color": "00ff04ff" + }, + { + "name": "front-foot-tip", + "parent": "front-foot", + "length": 56.03, + "rotation": -1.68, + "x": 41.42, + "y": -0.09, + "transform": "noRotationOrReflection", + "color": "00ff04ff" + }, + { "name": "back-shoulder", "parent": "torso3", "rotation": -104.11, "x": 7.32, "y": -19.22, "color": "ff000dff" }, + { "name": "rear-upper-arm", "parent": "back-shoulder", "length": 51.94, "rotation": -65.45, "color": "ff000dff" }, + { "name": "rear-bracer", "parent": "rear-upper-arm", "length": 34.56, "rotation": 23.15, "x": 51.36, "color": "ff000dff" }, + { + "name": "gun", + "parent": "rear-bracer", + "length": 43.11, + "rotation": -5.43, + "x": 34.42, + "y": -0.45, + "color": "ff000dff" + }, + { "name": "gun-tip", "parent": "gun", "rotation": 7.1, "x": 200.78, "y": 52.5, "color": "ff0000ff" }, + { + "name": "neck", + "parent": "torso3", + "length": 25.45, + "rotation": -31.54, + "x": 42.46, + "y": -0.31, + "color": "e0da19ff" + }, + { + "name": "head", + "parent": "neck", + "length": 131.79, + "rotation": 26.1, + "x": 27.66, + "y": -0.26, + "color": "e0da19ff" + }, + { + "name": "hair1", + "parent": "head", + "length": 47.23, + "rotation": -49.1, + "x": 149.83, + "y": -59.77, + "color": "e0da19ff" + }, + { + "name": "hair2", + "parent": "hair1", + "length": 55.57, + "rotation": 50.42, + "x": 47.23, + "y": 0.19, + "color": "e0da19ff" + }, + { + "name": "hair3", + "parent": "head", + "length": 62.22, + "rotation": -32.17, + "x": 164.14, + "y": 3.68, + "color": "e0da19ff" + }, + { + "name": "hair4", + "parent": "hair3", + "length": 80.28, + "rotation": 83.71, + "x": 62.22, + "y": -0.04, + "color": "e0da19ff" + }, + { "name": "hoverboard-thruster-front", "parent": "hoverboard-controller", "rotation": -29.2, "x": 95.77, "y": -2.99, "transform": "noRotationOrReflection" }, + { "name": "hoverboard-thruster-rear", "parent": "hoverboard-controller", "rotation": -29.2, "x": -76.47, "y": -4.88, "transform": "noRotationOrReflection" }, + { "name": "hoverglow-front", "parent": "hoverboard-thruster-front", "rotation": 0.17, "x": -1.78, "y": -37.79 }, + { "name": "hoverglow-rear", "parent": "hoverboard-thruster-rear", "rotation": 0.17, "x": 1.06, "y": -35.66 }, + { "name": "muzzle", "parent": "rear-bracer", "rotation": 3.06, "x": 242.34, "y": 34.26, "color": "ffb900ff" }, + { "name": "muzzle-ring", "parent": "muzzle", "color": "ffb900ff" }, + { "name": "muzzle-ring2", "parent": "muzzle", "color": "ffb900ff" }, + { "name": "muzzle-ring3", "parent": "muzzle", "color": "ffb900ff" }, + { "name": "muzzle-ring4", "parent": "muzzle", "color": "ffb900ff" }, + { "name": "portal", "parent": "portal-root" }, + { "name": "portal-shade", "parent": "portal-root" }, + { "name": "portal-streaks1", "parent": "portal-root" }, + { "name": "portal-streaks2", "parent": "portal-root" }, + { "name": "side-glow1", "parent": "hoverboard-controller", "x": -110.56, "y": 2.62, "color": "000effff" }, + { + "name": "side-glow2", + "parent": "hoverboard-controller", + "x": -110.56, + "y": 2.62, + "scaleX": 0.738, + "scaleY": 0.738, + "color": "000effff" + }, + { "name": "head-control", "parent": "head", "x": 110.21, "color": "00a220ff" } +], +"slots": [ + { "name": "portal-bg", "bone": "portal" }, + { "name": "portal-shade", "bone": "portal-shade" }, + { "name": "portal-streaks2", "bone": "portal-streaks2", "blend": "additive" }, + { "name": "portal-streaks1", "bone": "portal-streaks1", "blend": "additive" }, + { "name": "portal-flare8", "bone": "flare8", "color": "c3cbffff", "blend": "additive" }, + { "name": "portal-flare9", "bone": "flare9", "color": "c3cbffff", "blend": "additive" }, + { "name": "portal-flare10", "bone": "flare10", "color": "c3cbffff", "blend": "additive" }, + { "name": "clipping", "bone": "clipping" }, + { "name": "exhaust3", "bone": "exhaust3", "color": "5eb4ffff", "blend": "additive" }, + { "name": "hoverboard-thruster-rear", "bone": "hoverboard-thruster-rear" }, + { "name": "hoverboard-thruster-front", "bone": "hoverboard-thruster-front" }, + { "name": "hoverboard-board", "bone": "hoverboard-controller" }, + { "name": "side-glow1", "bone": "side-glow1", "color": "ff8686ff", "blend": "additive" }, + { "name": "side-glow3", "bone": "side-glow1", "color": "ff8686ff", "blend": "additive" }, + { "name": "side-glow2", "bone": "side-glow2", "color": "ff8686ff", "blend": "additive" }, + { "name": "hoverglow-front", "bone": "hoverglow-front", "color": "5eb4ffff", "blend": "additive" }, + { "name": "hoverglow-rear", "bone": "hoverglow-rear", "color": "5eb4ffff", "blend": "additive" }, + { "name": "exhaust1", "bone": "exhaust2", "color": "5eb4ffff", "blend": "additive" }, + { "name": "exhaust2", "bone": "exhaust1", "color": "5eb4ffff", "blend": "additive" }, + { "name": "rear-upper-arm", "bone": "rear-upper-arm", "attachment": "rear-upper-arm" }, + { "name": "rear-bracer", "bone": "rear-bracer", "attachment": "rear-bracer" }, + { "name": "gun", "bone": "gun", "attachment": "gun" }, + { "name": "rear-foot", "bone": "rear-foot", "attachment": "rear-foot" }, + { "name": "rear-thigh", "bone": "rear-thigh", "attachment": "rear-thigh" }, + { "name": "rear-shin", "bone": "rear-shin", "attachment": "rear-shin" }, + { "name": "neck", "bone": "neck", "attachment": "neck" }, + { "name": "torso", "bone": "torso", "attachment": "torso" }, + { "name": "front-upper-arm", "bone": "front-upper-arm", "attachment": "front-upper-arm" }, + { "name": "head", "bone": "head", "attachment": "head" }, + { "name": "eye", "bone": "head", "attachment": "eye-indifferent" }, + { "name": "front-thigh", "bone": "front-thigh", "attachment": "front-thigh" }, + { "name": "front-foot", "bone": "front-foot", "attachment": "front-foot" }, + { "name": "front-shin", "bone": "front-shin", "attachment": "front-shin" }, + { "name": "mouth", "bone": "head", "attachment": "mouth-smile" }, + { "name": "goggles", "bone": "head", "attachment": "goggles" }, + { "name": "front-bracer", "bone": "front-bracer", "attachment": "front-bracer" }, + { "name": "front-fist", "bone": "front-fist", "attachment": "front-fist-closed" }, + { "name": "muzzle", "bone": "muzzle" }, + { "name": "head-bb", "bone": "head" }, + { "name": "portal-flare1", "bone": "flare1", "color": "c3cbffff", "blend": "additive" }, + { "name": "portal-flare2", "bone": "flare2", "color": "c3cbffff", "blend": "additive" }, + { "name": "portal-flare3", "bone": "flare3", "color": "c3cbffff", "blend": "additive" }, + { "name": "portal-flare4", "bone": "flare4", "color": "c3cbffff", "blend": "additive" }, + { "name": "portal-flare5", "bone": "flare5", "color": "c3cbffff", "blend": "additive" }, + { "name": "portal-flare6", "bone": "flare6", "color": "c3cbffff", "blend": "additive" }, + { "name": "portal-flare7", "bone": "flare7", "color": "c3cbffff", "blend": "additive" }, + { "name": "crosshair", "bone": "crosshair" }, + { "name": "muzzle-glow", "bone": "gun-tip", "color": "ffffff00", "blend": "additive" }, + { "name": "muzzle-ring", "bone": "muzzle-ring", "color": "d8baffff", "blend": "additive" }, + { "name": "muzzle-ring2", "bone": "muzzle-ring2", "color": "d8baffff", "blend": "additive" }, + { "name": "muzzle-ring3", "bone": "muzzle-ring3", "color": "d8baffff", "blend": "additive" }, + { "name": "muzzle-ring4", "bone": "muzzle-ring4", "color": "d8baffff", "blend": "additive" } +], +"ik": [ + { + "name": "aim-ik", + "order": 13, + "bones": [ "rear-upper-arm" ], + "target": "crosshair", + "mix": 0 + }, + { + "name": "aim-torso-ik", + "order": 8, + "bones": [ "aim-constraint-target" ], + "target": "crosshair" + }, + { + "name": "board-ik", + "order": 1, + "bones": [ "hoverboard-controller" ], + "target": "board-ik" + }, + { + "name": "front-foot-ik", + "order": 6, + "bones": [ "front-foot" ], + "target": "front-foot-target" + }, + { + "name": "front-leg-ik", + "order": 4, + "bones": [ "front-thigh", "front-shin" ], + "target": "front-leg-target", + "bendPositive": false + }, + { + "name": "rear-foot-ik", + "order": 7, + "bones": [ "rear-foot" ], + "target": "rear-foot-target" + }, + { + "name": "rear-leg-ik", + "order": 5, + "bones": [ "rear-thigh", "rear-shin" ], + "target": "rear-leg-target", + "bendPositive": false + } +], +"transform": [ + { + "name": "aim-front-arm-transform", + "order": 11, + "bones": [ "front-upper-arm" ], + "target": "aim-constraint-target", + "rotation": -180, + "mixRotate": 0, + "mixX": 0, + "mixScaleX": 0, + "mixShearY": 0 + }, + { + "name": "aim-head-transform", + "order": 10, + "bones": [ "head" ], + "target": "aim-constraint-target", + "rotation": 84.3, + "mixRotate": 0, + "mixX": 0, + "mixScaleX": 0, + "mixShearY": 0 + }, + { + "name": "aim-rear-arm-transform", + "order": 12, + "bones": [ "rear-upper-arm" ], + "target": "aim-constraint-target", + "x": 57.7, + "y": 56.4, + "mixRotate": 0, + "mixX": 0, + "mixScaleX": 0, + "mixShearY": 0 + }, + { + "name": "aim-torso-transform", + "order": 9, + "bones": [ "torso" ], + "target": "aim-constraint-target", + "rotation": 69.5, + "shearY": -36, + "mixRotate": 0, + "mixX": 0, + "mixScaleX": 0, + "mixShearY": 0 + }, + { + "name": "front-foot-board-transform", + "order": 2, + "bones": [ "front-foot-target" ], + "target": "hoverboard-controller", + "x": -69.8, + "y": 20.7, + "mixRotate": 0, + "mixX": 0, + "mixScaleX": 0, + "mixShearY": 0 + }, + { + "name": "rear-foot-board-transform", + "order": 3, + "bones": [ "rear-foot-target" ], + "target": "hoverboard-controller", + "x": 86.6, + "y": 21.3, + "mixRotate": 0, + "mixX": 0, + "mixScaleX": 0, + "mixShearY": 0 + }, + { + "name": "shoulder", + "bones": [ "back-shoulder" ], + "target": "front-shoulder", + "x": 40.17, + "y": -1.66, + "mixRotate": 0, + "mixX": -1, + "mixScaleX": 0, + "mixShearY": 0 + }, + { + "name": "toes-board", + "order": 14, + "bones": [ "front-foot-tip", "back-foot-tip" ], + "target": "hoverboard-controller", + "mixRotate": 0, + "mixX": 0, + "mixScaleX": 0, + "mixShearY": 0 + } +], +"skins": [ + { + "name": "default", + "attachments": { + "clipping": { + "clipping": { + "type": "clipping", + "end": "head-bb", + "vertexCount": 9, + "vertices": [ 66.76, 509.48, 19.98, 434.54, 5.34, 336.28, 22.19, 247.93, 77.98, 159.54, 182.21, -97.56, 1452.26, -99.8, 1454.33, 843.61, 166.57, 841.02 ], + "color": "ce3a3aff" + } + }, + "crosshair": { + "crosshair": { "width": 89, "height": 89 } + }, + "exhaust1": { + "hoverglow-small": { "scaleX": 0.4629, "scaleY": 0.8129, "rotation": -83.07, "width": 274, "height": 75 } + }, + "exhaust2": { + "hoverglow-small": { + "x": 0.01, + "y": -0.76, + "scaleX": 0.4208, + "scaleY": 0.8403, + "rotation": -89.25, + "width": 274, + "height": 75 + } + }, + "exhaust3": { + "hoverglow-small": { "scaleX": 0.4629, "scaleY": 0.8129, "rotation": -83.07, "width": 274, "height": 75 } + }, + "eye": { + "eye-indifferent": { + "type": "mesh", + "uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ], + "triangles": [ 1, 3, 0, 1, 2, 3 ], + "vertices": [ 2, 66, -36.8, -91.35, 0.3, 46, 73.41, -91.35, 0.7, 2, 66, -87.05, -13.11, 0.70968, 46, 23.16, -13.11, 0.29032, 2, 66, -12.18, 34.99, 0.82818, 46, 98.03, 34.99, 0.17182, 2, 66, 38.07, -43.25, 0.59781, 46, 148.28, -43.25, 0.40219 ], + "hull": 4, + "edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ], + "width": 93, + "height": 89 + }, + "eye-surprised": { + "type": "mesh", + "uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ], + "triangles": [ 1, 2, 3, 1, 3, 0 ], + "vertices": [ 2, 66, -46.74, -89.7, 0.3, 46, 63.47, -89.7, 0.7, 2, 66, -77.58, -1.97, 0.71, 46, 32.63, -1.97, 0.29, 2, 66, 6.38, 27.55, 0.83, 46, 116.59, 27.55, 0.17, 2, 66, 37.22, -60.19, 0.6, 46, 147.44, -60.19, 0.4 ], + "hull": 4, + "edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ], + "width": 93, + "height": 89 + } + }, + "front-bracer": { + "front-bracer": { "x": 12.03, "y": -1.68, "rotation": 79.6, "width": 58, "height": 80 } + }, + "front-fist": { + "front-fist-closed": { "x": 35.5, "y": 6, "rotation": 67.16, "width": 75, "height": 82 }, + "front-fist-open": { "x": 39.57, "y": 7.76, "rotation": 67.16, "width": 86, "height": 87 } + }, + "front-foot": { + "front-foot": { + "type": "mesh", + "uvs": [ 0.59417, 0.23422, 0.62257, 0.30336, 0.6501, 0.37036, 0.67637, 0.38404, 0.72068, 0.4071, 0.76264, 0.42894, 1, 0.70375, 1, 1, 0.65517, 1, 0.46923, 0.99999, 0, 1, 0, 0.39197, 0.17846, 0, 0.49796, 0 ], + "triangles": [ 8, 9, 3, 4, 8, 3, 5, 8, 4, 6, 8, 5, 8, 6, 7, 11, 1, 10, 0, 12, 13, 0, 11, 12, 0, 1, 11, 9, 2, 3, 1, 2, 10, 9, 10, 2 ], + "vertices": [ 2, 38, 18.17, 41.57, 0.7896, 39, 12.46, 46.05, 0.2104, 2, 38, 24.08, 40.76, 0.71228, 39, 16.12, 41.34, 0.28772, 2, 38, 29.81, 39.98, 0.55344, 39, 19.67, 36.78, 0.44656, 2, 38, 32.81, 41.67, 0.38554, 39, 23, 35.89, 0.61446, 2, 38, 37.86, 44.52, 0.25567, 39, 28.61, 34.4, 0.74433, 2, 38, 42.65, 47.22, 0.17384, 39, 33.92, 32.99, 0.82616, 1, 39, 64.15, 14.56, 1, 1, 39, 64.51, -5.87, 1, 1, 39, 21.08, -6.64, 1, 2, 38, 44.67, -6.77, 0.5684, 39, -2.34, -6.97, 0.4316, 1, 38, 3.1, -48.81, 1, 1, 38, -26.73, -19.31, 1, 1, 38, -30.15, 15.69, 1, 1, 38, -1.84, 44.32, 1 ], + "hull": 14, + "edges": [ 14, 16, 16, 18, 18, 20, 4, 18, 20, 22, 24, 26, 22, 24, 12, 14, 10, 12, 2, 4, 2, 20, 4, 6, 6, 16, 2, 0, 0, 26, 6, 8, 8, 10 ], + "width": 126, + "height": 69 + } + }, + "front-shin": { + "front-shin": { + "type": "mesh", + "uvs": [ 0.90031, 0.05785, 1, 0.12828, 1, 0.21619, 0.9025, 0.31002, 0.78736, 0.35684, 0.78081, 0.39874, 0.77215, 0.45415, 0.77098, 0.51572, 0.84094, 0.63751, 0.93095, 0.7491, 0.95531, 0.7793, 0.78126, 0.87679, 0.5613, 1, 0.2687, 1, 0, 1, 0.00279, 0.96112, 0.01358, 0.81038, 0.02822, 0.60605, 0.08324, 0.45142, 0.18908, 0.31882, 0.29577, 0.2398, 0.30236, 0.14941, 0.37875, 0.05902, 0.53284, 0, 0.70538, 0, 0.41094, 0.71968, 0.40743, 0.54751, 0.41094, 0.4536, 0.4724, 0.35186, 0.33367, 0.27829, 0.50226, 0.31664, 0.65328, 0.67507, 0.60762, 0.52716, 0.6006, 0.45125, 0.62747, 0.37543, 0.6573, 0.3385, 0.27843, 0.32924, 0.18967, 0.45203, 0.16509, 0.58586, 0.18265, 0.7682, 0.50532, 0.24634, 0.59473, 0.17967, 0.60161, 0.10611, 0.51392, 0.04327, 0.72198, 0.28849, 0.82343, 0.20266, 0.86814, 0.11377, 0.79592, 0.04634, 0.44858, 0.15515, 0.25466, 0.96219, 0.53169, 0.9448, 0.7531, 0.8324 ], + "triangles": [ 24, 0, 47, 43, 23, 24, 47, 43, 24, 43, 22, 23, 42, 43, 47, 46, 47, 0, 42, 47, 46, 46, 0, 1, 48, 22, 43, 48, 43, 42, 21, 22, 48, 41, 48, 42, 45, 42, 46, 41, 42, 45, 46, 1, 2, 45, 46, 2, 40, 48, 41, 48, 20, 21, 29, 48, 40, 29, 20, 48, 44, 41, 45, 40, 41, 44, 3, 45, 2, 44, 45, 3, 30, 29, 40, 35, 30, 40, 36, 19, 20, 36, 20, 29, 44, 35, 40, 28, 29, 30, 4, 44, 3, 35, 44, 4, 34, 30, 35, 5, 35, 4, 34, 28, 30, 33, 28, 34, 37, 19, 36, 18, 19, 37, 27, 29, 28, 27, 28, 33, 36, 29, 27, 37, 36, 27, 5, 34, 35, 6, 34, 5, 33, 34, 6, 6, 32, 33, 7, 32, 6, 26, 37, 27, 38, 18, 37, 38, 37, 26, 17, 18, 38, 31, 32, 7, 31, 7, 8, 32, 25, 26, 38, 26, 25, 27, 33, 32, 32, 26, 27, 39, 38, 25, 17, 38, 39, 16, 17, 39, 51, 31, 8, 51, 8, 9, 11, 51, 9, 11, 9, 10, 31, 50, 25, 31, 25, 32, 50, 31, 51, 49, 39, 25, 49, 25, 50, 15, 16, 39, 49, 15, 39, 13, 49, 50, 14, 15, 49, 13, 14, 49, 12, 50, 51, 12, 51, 11, 13, 50, 12 ], + "vertices": [ -23.66, 19.37, -11.73, 28.98, 4.34, 30.83, 22.41, 24.87, 32.05, 16.48, 39.77, 16.83, 49.98, 17.3, 61.25, 18.5, 82.85, 26.78, 102.4, 36.46, 107.69, 39.09, 127.15, 26.97, 151.74, 11.65, 154.49, -12.18, 157.02, -34.07, 149.89, -34.66, 122.23, -36.97, 84.75, -40.09, 55.97, -38.88, 30.73, -33.05, 15.29, -26.03, -1.3, -27.41, -18.54, -23.09, -30.78, -11.79, -32.4, 2.27, 101.92, -6.52, 70.48, -10.44, 53.28, -12.14, 34.11, -9.28, 21.96, -22.13, 27.39, -7.59, 91.48, 12.28, 64.88, 5.44, 51.07, 3.26, 36.95, 3.85, 29.92, 5.5, 31.8, -25.56, 55.08, -30.19, 79.77, -29.37, 112.93, -24.09, 14.51, -8.83, 1.48, -2.95, -12.03, -3.94, -22.69, -12.41, 20.17, 9.71, 3.53, 16.16, -13.14, 17.93, -24.78, 10.62, -1.62, -15.37, 147.71, -14.13, 141.93, 8.07, 119.3, 23.74 ], + "hull": 25, + "edges": [ 8, 6, 6, 4, 4, 2, 2, 0, 0, 48, 46, 48, 46, 44, 44, 42, 42, 40, 40, 38, 38, 36, 36, 34, 32, 34, 50, 52, 52, 54, 54, 56, 40, 58, 58, 60, 8, 10, 20, 22, 22, 24, 62, 64, 64, 66, 66, 68, 8, 70, 70, 60, 68, 70, 58, 72, 72, 74, 74, 76, 76, 78, 24, 26, 26, 28, 58, 80, 80, 82, 82, 84, 84, 86, 86, 44, 70, 88, 88, 90, 90, 92, 92, 94, 94, 48, 80, 88, 88, 6, 82, 90, 90, 4, 84, 92, 92, 2, 86, 94, 94, 0, 56, 60, 10, 12, 12, 14, 14, 16, 28, 30, 30, 32, 26, 98, 98, 78, 30, 98, 24, 100, 100, 50, 98, 100, 22, 102, 102, 62, 100, 102, 16, 18, 18, 20, 102, 18 ], + "width": 82, + "height": 184 + } + }, + "front-thigh": { + "front-thigh": { "x": 42.48, "y": 4.45, "rotation": 84.87, "width": 45, "height": 112 } + }, + "front-upper-arm": { + "front-upper-arm": { "x": 28.31, "y": 7.37, "rotation": 97.9, "width": 46, "height": 97 } + }, + "goggles": { + "goggles": { + "type": "mesh", + "uvs": [ 0.53653, 0.04114, 0.72922, 0.16036, 0.91667, 0.33223, 0.97046, 0.31329, 1, 0.48053, 0.95756, 0.5733, 0.88825, 0.6328, 0.86878, 0.78962, 0.77404, 0.8675, 0.72628, 1, 0.60714, 0.93863, 0.49601, 0.88138, 0.41558, 0.75027, 0.32547, 0.70084, 0.2782, 0.58257, 0.1721, 0.63281, 0.17229, 0.75071, 0.10781, 0.79898, 0, 0.32304, 0, 0.12476, 0.07373, 0.07344, 0.15423, 0.10734, 0.23165, 0.13994, 0.30313, 0.02256, 0.34802, 0, 0.42979, 0.69183, 0.39476, 0.51042, 0.39488, 0.31512, 0.45878, 0.23198, 0.56501, 0.28109, 0.69961, 0.39216, 0.82039, 0.54204, 0.85738, 0.62343, 0.91107, 0.51407, 0.72639, 0.32147, 0.58764, 0.19609, 0.48075, 0.11269, 0.37823, 0.05501, 0.3287, 0.17866, 0.319, 0.305, 0.36036, 0.53799, 0.40327, 0.70072, 0.30059, 0.55838, 0.21957, 0.2815, 0.09963, 0.28943, 0.56863, 0.4368, 0.4911, 0.37156, 0.51185, 0.52093, 0.67018, 0.59304, 0.7619, 0.68575, 0.73296, 0.43355 ], + "triangles": [ 18, 44, 15, 21, 19, 20, 17, 18, 15, 44, 19, 21, 2, 3, 4, 18, 19, 44, 2, 33, 34, 33, 2, 4, 5, 33, 4, 5, 6, 33, 7, 32, 6, 31, 50, 33, 32, 31, 33, 6, 32, 33, 31, 49, 50, 49, 31, 32, 49, 32, 7, 8, 49, 7, 33, 50, 34, 17, 15, 16, 9, 48, 8, 49, 48, 50, 50, 48, 45, 47, 45, 48, 50, 45, 30, 45, 47, 46, 45, 46, 29, 30, 45, 29, 30, 29, 34, 30, 34, 50, 47, 26, 46, 25, 10, 11, 12, 25, 11, 41, 12, 42, 42, 44, 43, 43, 21, 22, 41, 40, 25, 41, 42, 40, 29, 35, 34, 40, 26, 25, 25, 26, 47, 37, 24, 0, 36, 37, 0, 42, 43, 39, 42, 39, 40, 28, 38, 36, 40, 39, 26, 28, 27, 38, 26, 39, 27, 37, 38, 23, 39, 43, 38, 38, 37, 36, 27, 39, 38, 43, 22, 38, 37, 23, 24, 22, 23, 38, 36, 0, 35, 28, 36, 35, 29, 28, 35, 27, 28, 46, 26, 27, 46, 35, 0, 1, 34, 35, 1, 12, 41, 25, 47, 10, 25, 44, 21, 43, 42, 14, 44, 14, 15, 44, 13, 14, 42, 12, 13, 42, 46, 28, 29, 47, 48, 10, 48, 9, 10, 49, 8, 48, 2, 34, 1 ], + "vertices": [ 2, 66, 61.88, 22.81, 0.832, 46, 172.09, 22.81, 0.168, 2, 66, 59.89, -31.19, 0.6855, 46, 170.1, -31.19, 0.3145, 2, 66, 49.2, -86.8, 0.32635, 46, 159.41, -86.8, 0.67365, 2, 66, 56.82, -99.01, 0.01217, 46, 167.03, -99.01, 0.98783, 1, 46, 143.4, -115.48, 1, 2, 66, 15, -110.14, 0.0041, 46, 125.21, -110.14, 0.9959, 2, 66, -0.32, -96.36, 0.07948, 46, 109.89, -96.36, 0.92052, 2, 66, -26.56, -100.19, 0.01905, 46, 83.65, -100.19, 0.98095, 2, 66, -46.96, -81.16, 0.4921, 46, 63.26, -81.16, 0.50791, 2, 66, -71.84, -76.69, 0.56923, 46, 38.37, -76.69, 0.43077, 2, 66, -72.54, -43.98, 0.74145, 46, 37.67, -43.98, 0.25855, 2, 66, -73.2, -13.47, 0.87929, 46, 37.01, -13.47, 0.12071, 2, 66, -59.63, 13.55, 0.864, 46, 50.58, 13.55, 0.136, 2, 66, -59.69, 38.45, 0.85289, 46, 50.52, 38.45, 0.14711, 2, 66, -45.26, 56.6, 0.74392, 46, 64.95, 56.6, 0.25608, 2, 66, -62.31, 79.96, 0.624, 46, 47.9, 79.96, 0.376, 2, 66, -80.76, 73.42, 0.616, 46, 29.45, 73.42, 0.384, 2, 66, -93.9, 86.64, 0.288, 46, 16.31, 86.64, 0.712, 1, 46, 81.51, 139.38, 1, 1, 46, 112.56, 150.3, 1, 2, 66, 16.76, 134.97, 0.02942, 46, 126.97, 134.97, 0.97058, 2, 66, 18.42, 113.28, 0.36147, 46, 128.63, 113.28, 0.63853, 2, 66, 20.02, 92.43, 0.7135, 46, 130.23, 92.43, 0.2865, 2, 66, 44.58, 81.29, 0.69603, 46, 154.79, 81.29, 0.30397, 2, 66, 52, 71.48, 0.848, 46, 162.21, 71.48, 0.152, 2, 66, -49.25, 13.27, 0.8, 46, 60.96, 13.27, 0.2, 2, 66, -23.88, 31.88, 0.896, 46, 86.33, 31.88, 0.104, 2, 66, 6.72, 42.6, 0.928, 46, 116.93, 42.6, 0.072, 2, 66, 25.26, 31.44, 0.8, 46, 135.47, 31.44, 0.2, 2, 66, 26.77, 2.59, 0.75, 46, 136.98, 2.59, 0.25, 2, 66, 21.02, -36.66, 0.54887, 46, 131.23, -36.66, 0.45113, 2, 66, 8.01, -74.65, 0.36029, 46, 118.22, -74.65, 0.63971, 2, 66, -1.52, -88.24, 0.1253, 46, 108.69, -88.24, 0.8747, 2, 66, 20.25, -95.44, 0.08687, 46, 130.46, -95.44, 0.91313, 2, 66, 34.42, -39.36, 0.72613, 46, 144.63, -39.36, 0.27387, 2, 66, 42.03, 1.7, 0.824, 46, 152.25, 1.7, 0.176, 2, 66, 45.85, 32.6, 0.856, 46, 156.06, 32.6, 0.144, 1, 66, 46.01, 61.02, 1, 1, 66, 22.35, 66.41, 1, 1, 66, 1.73, 61.84, 1, 2, 66, -31.17, 38.83, 0.928, 46, 79.04, 38.83, 0.072, 2, 66, -52.94, 19.31, 0.79073, 46, 57.27, 19.31, 0.20927, 2, 66, -39.54, 52.42, 0.912, 46, 70.67, 52.42, 0.088, 2, 66, -3.2, 87.61, 0.744, 46, 107.02, 87.61, 0.256, 2, 66, -14.82, 116.7, 0.6368, 46, 95.4, 116.7, 0.3632, 2, 66, 2.7, -6.87, 0.856, 46, 112.91, -6.87, 0.144, 2, 66, 6.21, 15.8, 0.744, 46, 116.42, 15.8, 0.256, 2, 66, -15.39, 2.47, 0.856, 46, 94.82, 2.47, 0.144, 2, 66, -12.98, -40.48, 0.72102, 46, 97.24, -40.48, 0.27898, 2, 66, -19.55, -68.16, 0.59162, 46, 90.66, -68.16, 0.40838, 2, 66, 17.44, -47.15, 0.53452, 46, 127.65, -47.15, 0.46548 ], + "hull": 25, + "edges": [ 36, 34, 34, 32, 32, 30, 30, 28, 28, 26, 26, 24, 24, 22, 18, 16, 16, 14, 14, 12, 12, 10, 10, 8, 8, 6, 6, 4, 4, 2, 2, 0, 0, 48, 48, 46, 46, 44, 36, 38, 40, 38, 24, 50, 50, 52, 52, 54, 54, 56, 56, 58, 58, 60, 62, 64, 64, 12, 8, 66, 66, 68, 68, 70, 70, 72, 72, 74, 74, 76, 76, 78, 78, 80, 80, 82, 82, 24, 24, 84, 84, 86, 86, 44, 40, 42, 42, 44, 42, 88, 88, 30, 58, 90, 90, 92, 92, 94, 18, 20, 20, 22, 94, 20, 18, 96, 96, 98, 60, 100, 100, 62, 98, 100 ], + "width": 261, + "height": 166 + } + }, + "gun": { + "gun": { "x": 77.3, "y": 16.4, "rotation": 60.83, "width": 210, "height": 203 } + }, + "head": { + "head": { + "type": "mesh", + "uvs": [ 0.75919, 0.06107, 0.88392, 0.17893, 0.90174, 0.30856, 0.94224, 0.1966, 1, 0.26584, 1, 0.422, 0.95864, 0.46993, 0.92118, 0.51333, 0.85957, 0.5347, 0.78388, 0.65605, 0.74384, 0.74838, 0.85116, 0.75151, 0.84828, 0.82564, 0.81781, 0.85367, 0.75599, 0.85906, 0.76237, 0.90468, 0.65875, 1, 0.38337, 1, 0.1858, 0.85404, 0.12742, 0.81091, 0.06025, 0.69209, 0, 0.58552, 0, 0.41021, 0.0853, 0.20692, 0.24243, 0.14504, 0.5, 0.1421, 0.50324, 0.07433, 0.41738, 0, 0.57614, 0, 0.85059, 0.36087, 0.73431, 0.43206, 0.68481, 0.31271, 0.72165, 0.16718, 0.55931, 0.04154, 0.44764, 0.22895, 0.23926, 0.26559, 0.71272, 0.44036, 0.56993, 0.383, 0.41678, 0.33511, 0.293, 0.31497, 0.70802, 0.44502, 0.56676, 0.38976, 0.41521, 0.34416, 0.28754, 0.33017, 0.88988, 0.50177, 0.30389, 0.73463, 0.2646, 0.65675, 0.21414, 0.61584, 0.14613, 0.62194, 0.10316, 0.66636, 0.10358, 0.72557, 0.14505, 0.79164, 0.20263, 0.81355, 0.27873, 0.80159, 0.34947, 0.7376, 0.23073, 0.57073, 0.08878, 0.60707, 0.29461, 0.8129, 0.73006, 0.87883, 0.69805, 0.87348, 0.66166, 0.79681, 0.22468, 0.69824, 0.14552, 0.67405 ], + "triangles": [ 50, 49, 62, 34, 25, 31, 39, 35, 34, 38, 39, 34, 37, 38, 34, 42, 39, 38, 43, 39, 42, 32, 2, 31, 31, 37, 34, 42, 38, 37, 41, 42, 37, 43, 22, 39, 30, 31, 29, 36, 37, 31, 30, 36, 31, 40, 41, 37, 36, 40, 37, 36, 30, 44, 55, 22, 43, 55, 48, 56, 47, 48, 55, 46, 55, 54, 42, 55, 43, 47, 55, 46, 62, 49, 48, 61, 47, 46, 62, 48, 47, 61, 62, 47, 46, 54, 45, 42, 41, 55, 61, 46, 45, 55, 41, 54, 61, 51, 50, 61, 50, 62, 60, 41, 40, 54, 41, 60, 53, 61, 45, 52, 51, 61, 57, 53, 45, 57, 45, 54, 53, 52, 61, 52, 19, 51, 57, 18, 52, 57, 52, 53, 17, 54, 60, 57, 54, 17, 18, 57, 17, 19, 50, 51, 33, 27, 28, 26, 27, 33, 0, 33, 28, 32, 33, 0, 32, 0, 1, 33, 25, 26, 33, 32, 25, 31, 25, 32, 2, 32, 1, 2, 3, 4, 29, 31, 2, 2, 4, 5, 29, 2, 5, 6, 29, 5, 30, 29, 6, 44, 30, 6, 18, 19, 52, 49, 56, 48, 34, 24, 25, 35, 23, 24, 35, 24, 34, 39, 22, 35, 22, 23, 35, 7, 44, 6, 8, 36, 44, 40, 36, 8, 8, 44, 7, 56, 21, 22, 55, 56, 22, 9, 40, 8, 20, 21, 56, 20, 56, 49, 9, 60, 40, 10, 60, 9, 20, 50, 19, 12, 10, 11, 13, 10, 12, 14, 60, 10, 13, 14, 10, 59, 60, 14, 58, 59, 14, 58, 14, 15, 16, 17, 60, 59, 16, 60, 15, 16, 59, 15, 59, 58, 20, 49, 50 ], + "vertices": [ 2, 50, 41.97, -41.8, 0.94074, 66, 165.41, -22.6, 0.05926, 4, 48, 73.47, 27.54, 0.26795, 50, -5.75, -51.71, 0.4738, 49, 112.99, -11.41, 0.12255, 66, 143.5, -66.13, 0.1357, 4, 48, 38.23, 10.99, 0.6831, 50, -41.01, -35.22, 0.07866, 49, 92.73, -44.66, 0.04872, 66, 108.65, -83.49, 0.18952, 2, 48, 73.35, 10.89, 0.8455, 66, 143.77, -82.78, 0.1545, 2, 48, 58.59, -10.38, 0.91607, 66, 129.5, -104.39, 0.08393, 3, 46, 195.82, -119.82, 0.104, 47, 75.49, -4.55, 0.09191, 48, 14.36, -24.8, 0.80409, 4, 46, 178.62, -113.98, 0.19022, 47, 59.82, -13.72, 0.33409, 48, -2.7, -18.57, 0.46643, 66, 68.41, -113.98, 0.00926, 4, 46, 163.06, -108.69, 0.18724, 47, 45.64, -22.03, 0.3133, 48, -18.14, -12.93, 0.47469, 66, 52.84, -108.69, 0.02477, 2, 46, 151.52, -95.05, 0.91122, 66, 41.31, -95.05, 0.08878, 2, 46, 110.61, -87.69, 0.70564, 66, 0.4, -87.69, 0.29436, 2, 46, 81.05, -86.58, 0.63951, 66, -29.16, -86.58, 0.36049, 2, 46, 89.82, -114.32, 0.57, 66, -20.39, -114.32, 0.43, 2, 46, 68.72, -120.91, 0.57, 66, -41.49, -120.91, 0.43, 2, 46, 58.1, -115.9, 0.57, 66, -52.11, -115.9, 0.43, 2, 46, 51.03, -100.63, 0.64242, 66, -59.18, -100.63, 0.35758, 2, 46, 38.79, -106.76, 0.81659, 66, -71.43, -106.76, 0.18341, 2, 46, 2.68, -89.7, 0.77801, 66, -107.53, -89.7, 0.22199, 2, 46, -22.07, -19.3, 0.823, 66, -132.28, -19.3, 0.177, 2, 46, 1.2, 45.63, 0.51204, 66, -109.01, 45.63, 0.48796, 2, 46, 8.07, 64.81, 0.60869, 66, -102.14, 64.81, 0.39131, 2, 46, 35.44, 93.73, 0.80009, 66, -74.77, 93.73, 0.19991, 2, 46, 59.98, 119.66, 0.93554, 66, -50.23, 119.66, 0.06446, 2, 46, 109.26, 136.99, 0.99895, 66, -0.95, 136.99, 0.00105, 1, 46, 174.07, 135.27, 1, 3, 46, 205.59, 101.22, 0.80778, 49, -16.84, 104.63, 0.15658, 66, 95.38, 101.22, 0.03564, 3, 50, 58.94, 30.5, 0.43491, 49, 38.36, 61.89, 0.28116, 66, 119.35, 35.65, 0.28393, 2, 50, 75.56, 19.01, 0.92164, 66, 138.68, 41.52, 0.07836, 1, 50, 106.7, 26.9, 1, 1, 50, 83.79, -9.51, 1, 5, 47, 44.51, 27.24, 0.15139, 48, 19.12, 19.33, 0.44847, 50, -46.82, -15.19, 0.05757, 49, 72.19, -48.24, 0.1149, 66, 89.35, -75.58, 0.22767, 3, 47, 7.42, 19.08, 0.37772, 49, 34.32, -45.24, 0.09918, 66, 58.9, -52.89, 0.52311, 2, 49, 45.94, -9.07, 0.4826, 66, 87.99, -28.45, 0.5174, 2, 50, 20.62, -16.35, 0.7435, 66, 132.21, -23.49, 0.2565, 2, 50, 75.74, 0.94, 0.97172, 66, 152.95, 30.42, 0.02828, 4, 46, 200.45, 40.46, 0.18809, 50, 44.6, 56.29, 0.05831, 49, 11.15, 50.46, 0.14366, 66, 90.24, 40.46, 0.60994, 2, 46, 171.41, 90.12, 0.48644, 66, 61.2, 90.12, 0.51356, 2, 46, 164.84, -48.18, 0.43217, 66, 54.62, -48.18, 0.56783, 4, 46, 168.13, -6.02, 0.01949, 47, -28.65, 49.02, 0.02229, 49, 8.54, -6.09, 0.12791, 66, 57.92, -6.02, 0.83031, 2, 46, 167.84, 37.87, 0.15, 66, 57.63, 37.87, 0.85, 2, 46, 162.36, 71.5, 0.24107, 66, 52.15, 71.5, 0.75893, 2, 46, 163.11, -47.44, 0.41951, 66, 52.9, -47.44, 0.58049, 2, 46, 165.94, -5.87, 0.16355, 66, 55.73, -5.87, 0.83645, 2, 46, 165.14, 37.38, 0.15, 66, 54.93, 37.38, 0.85, 2, 46, 157.6, 71.4, 0.21735, 66, 47.39, 71.4, 0.78265, 3, 46, 163.5, -99.54, 0.61812, 47, 39.01, -15.71, 0.30445, 66, 53.29, -99.54, 0.07744, 2, 46, 45.38, 27.24, 0.16741, 66, -64.83, 27.24, 0.83259, 2, 46, 63.74, 44.98, 0.15, 66, -46.47, 44.98, 0.85, 2, 46, 70.7, 61.92, 0.22175, 66, -39.51, 61.92, 0.77825, 2, 46, 62.88, 78.71, 0.38, 66, -47.34, 78.71, 0.62, 2, 46, 46.53, 85.3, 0.51, 66, -63.68, 85.3, 0.49, 2, 46, 29.92, 79.34, 0.388, 66, -80.29, 79.34, 0.612, 2, 46, 15.08, 62.21, 0.38, 66, -95.13, 62.21, 0.62, 2, 46, 14.09, 45.32, 0.41, 66, -96.12, 45.32, 0.59, 2, 46, 24.3, 27.06, 0.192, 66, -85.91, 27.06, 0.808, 1, 66, -61.57, 15.3, 1, 2, 46, 84.87, 62.14, 0.16757, 66, -25.34, 62.14, 0.83243, 2, 46, 61.9, 94.84, 0.68145, 66, -48.31, 94.84, 0.31855, 2, 46, 22.54, 21.88, 0.16, 66, -87.67, 21.88, 0.84, 2, 46, 43.15, -95.95, 0.73445, 66, -67.06, -95.95, 0.26555, 2, 46, 41.77, -87.24, 0.67858, 66, -68.44, -87.24, 0.32142, 2, 46, 60.05, -70.36, 0.50195, 66, -50.16, -70.36, 0.49805, 2, 46, 48.49, 51.09, 0.25, 66, -61.72, 51.09, 0.75, 2, 46, 48.17, 73.71, 0.15634, 66, -62.04, 73.71, 0.84366 ], + "hull": 29, + "edges": [ 10, 8, 8, 6, 6, 4, 4, 2, 2, 0, 0, 56, 54, 56, 54, 52, 52, 50, 50, 48, 48, 46, 46, 44, 42, 44, 32, 34, 4, 58, 58, 60, 62, 64, 64, 66, 66, 54, 50, 68, 68, 70, 70, 44, 60, 72, 62, 74, 72, 74, 74, 76, 76, 78, 78, 44, 16, 80, 80, 82, 82, 84, 84, 86, 86, 44, 14, 88, 88, 72, 14, 16, 10, 12, 12, 14, 12, 60, 90, 92, 92, 94, 94, 96, 96, 98, 98, 100, 100, 102, 102, 104, 104, 106, 106, 90, 108, 110, 110, 112, 38, 40, 40, 42, 112, 40, 34, 36, 36, 38, 36, 114, 114, 108, 30, 32, 30, 28, 24, 26, 28, 26, 22, 24, 22, 20, 20, 18, 18, 16, 28, 116, 116, 118, 118, 120, 120, 20 ], + "width": 271, + "height": 298 + } + }, + "head-bb": { + "head": { + "type": "boundingbox", + "vertexCount": 6, + "vertices": [ -19.14, -70.3, 40.8, -118.08, 257.78, -115.62, 285.17, 57.18, 120.77, 164.95, -5.07, 76.95 ] + } + }, + "hoverboard-board": { + "hoverboard-board": { + "type": "mesh", + "uvs": [ 0.13865, 0.56624, 0.11428, 0.51461, 0.07619, 0.52107, 0.02364, 0.52998, 0.01281, 0.53182, 0, 0.37979, 0, 0.2206, 0.00519, 0.10825, 0.01038, 0.10726, 0.03834, 0.10194, 0.05091, 0, 0.08326, 0, 0.10933, 0.04206, 0.1382, 0.08865, 0.18916, 0.24067, 0.22234, 0.4063, 0.23886, 0.44063, 0.83412, 0.44034, 0.88444, 0.38296, 0.92591, 0.32639, 0.95996, 0.28841, 0.98612, 0.28542, 1, 0.38675, 0.99494, 0.47104, 0.97883, 0.53251, 0.94409, 0.62135, 0.90206, 0.69492, 0.86569, 0.71094, 0.82822, 0.70791, 0.81286, 0.77127, 0.62931, 0.77266, 0.61364, 0.70645, 0.47166, 0.70664, 0.45901, 0.77827, 0.27747, 0.76986, 0.2658, 0.70372, 0.24976, 0.71381, 0.24601, 0.77827, 0.23042, 0.84931, 0.20926, 0.90956, 0.17299, 1, 0.15077, 0.99967, 0.12906, 0.90192, 0.10369, 0.73693, 0.10198, 0.62482, 0.09131, 0.47272, 0.09133, 0.41325, 0.15082, 0.41868, 0.21991, 0.51856, 0.06331, 0.10816, 0.08383, 0.21696, 0.08905, 0.37532, 0.15903, 0.58726, 0.17538, 0.65706, 0.20118, 0.8029, 0.17918, 0.55644, 0.22166, 0.5802, 0.86259, 0.57962, 0.92346, 0.48534, 0.96691, 0.36881, 0.0945, 0.13259, 0.12688, 0.17831, 0.15986, 0.24682, 0.18036, 0.31268, 0.20607, 0.4235, 0.16074, 0.85403, 0.13624, 0.70122, 0.12096, 0.64049, 0.02396, 0.21811, 0.02732, 0.37839, 0.02557, 0.4972, 0.14476, 0.45736, 0.18019, 0.51689, 0.19692, 0.56636 ], + "triangles": [ 10, 11, 12, 9, 10, 12, 49, 9, 12, 60, 49, 12, 13, 60, 12, 61, 60, 13, 50, 49, 60, 50, 60, 61, 68, 8, 9, 68, 9, 49, 68, 49, 50, 7, 8, 68, 6, 7, 68, 61, 13, 14, 62, 61, 14, 50, 61, 62, 63, 62, 14, 59, 20, 21, 19, 20, 59, 51, 50, 62, 51, 62, 63, 51, 69, 68, 51, 68, 50, 6, 68, 69, 5, 6, 69, 18, 19, 59, 15, 63, 14, 59, 21, 22, 47, 51, 63, 47, 46, 51, 47, 63, 64, 15, 64, 63, 64, 15, 16, 71, 46, 47, 23, 59, 22, 69, 51, 70, 45, 46, 71, 70, 51, 2, 58, 18, 59, 58, 59, 23, 17, 18, 58, 70, 5, 69, 2, 51, 46, 1, 45, 71, 47, 48, 71, 47, 64, 48, 48, 72, 71, 1, 71, 72, 16, 48, 64, 45, 2, 46, 2, 45, 1, 70, 4, 5, 3, 70, 2, 3, 4, 70, 24, 58, 23, 72, 0, 1, 73, 55, 72, 55, 0, 72, 48, 73, 72, 57, 17, 58, 25, 57, 58, 56, 48, 16, 73, 48, 56, 56, 16, 17, 56, 17, 57, 52, 0, 55, 24, 25, 58, 44, 0, 52, 67, 44, 52, 52, 56, 53, 73, 52, 55, 56, 52, 73, 67, 52, 53, 26, 57, 25, 66, 67, 53, 56, 32, 35, 53, 56, 35, 56, 57, 32, 28, 31, 57, 57, 31, 32, 57, 27, 28, 26, 27, 57, 36, 53, 35, 43, 44, 67, 43, 67, 66, 34, 35, 32, 29, 31, 28, 30, 31, 29, 53, 54, 66, 53, 36, 54, 33, 34, 32, 37, 54, 36, 65, 43, 66, 38, 54, 37, 54, 65, 66, 39, 65, 54, 42, 43, 65, 38, 39, 54, 40, 42, 65, 40, 41, 42, 65, 39, 40 ], + "vertices": [ -189.36, 15.62, -201.35, 23.47, -220.09, 22.49, -245.95, 21.13, -251.28, 20.86, -257.58, 43.96, -257.57, 68.16, -255.02, 85.24, -252.47, 85.39, -238.71, 86.2, -232.52, 101.69, -216.61, 101.69, -203.78, 95.3, -189.58, 88.21, -164.51, 65.1, -148.19, 39.93, -140.06, 34.71, 152.82, 34.73, 177.57, 43.45, 197.97, 52.05, 214.72, 57.82, 227.6, 58.27, 234.42, 42.87, 231.94, 30.06, 224.01, 20.72, 206.91, 7.21, 186.23, -3.97, 168.34, -6.4, 149.9, -5.94, 142.35, -15.57, 52.04, -15.77, 44.33, -5.71, -25.52, -5.73, -31.75, -16.62, -121.07, -15.34, -126.81, -5.28, -134.7, -6.81, -136.54, -16.61, -144.22, -27.41, -154.63, -36.57, -172.47, -50.31, -183.41, -50.26, -194.09, -35.4, -206.56, -10.32, -207.4, 6.72, -212.65, 29.84, -212.64, 38.88, -183.37, 38.05, -149.38, 22.86, -226.43, 85.25, -216.33, 68.71, -213.76, 44.64, -179.34, 12.42, -171.29, 1.81, -158.6, -20.36, -169.42, 17.11, -148.52, 13.49, 166.82, 13.56, 196.76, 27.89, 218.14, 45.6, -211.08, 81.54, -195.15, 74.59, -178.93, 64.17, -168.84, 54.16, -156.19, 37.31, -178.5, -28.13, -190.55, -4.9, -198.07, 4.33, -245.79, 68.54, -244.14, 44.18, -245, 26.12, -186.36, 32.17, -168.92, 23.12, -160.69, 15.6 ], + "hull": 45, + "edges": [ 0, 2, 8, 10, 10, 12, 12, 14, 18, 20, 20, 22, 26, 28, 28, 30, 30, 32, 32, 34, 34, 36, 36, 38, 38, 40, 40, 42, 42, 44, 44, 46, 46, 48, 48, 50, 50, 52, 52, 54, 54, 56, 56, 58, 58, 60, 60, 62, 62, 64, 64, 66, 66, 68, 68, 70, 70, 72, 72, 74, 80, 82, 82, 84, 84, 86, 86, 88, 0, 88, 2, 90, 90, 92, 92, 94, 94, 96, 96, 32, 18, 98, 98, 100, 100, 102, 2, 4, 102, 4, 92, 102, 0, 104, 104, 106, 106, 108, 78, 80, 108, 78, 74, 76, 76, 78, 62, 56, 64, 70, 0, 110, 112, 114, 114, 116, 116, 118, 118, 42, 50, 116, 114, 34, 98, 120, 120, 122, 22, 24, 24, 26, 120, 24, 122, 124, 124, 126, 126, 128, 128, 96, 80, 130, 130, 132, 132, 134, 134, 88, 14, 16, 16, 18, 136, 16, 136, 138, 138, 140, 4, 6, 6, 8, 140, 6, 96, 112, 92, 142, 142, 144, 110, 146, 146, 112, 144, 146 ], + "width": 492, + "height": 152 + } + }, + "hoverboard-thruster-front": { + "hoverboard-thruster": { "x": 0.02, "y": -7.08, "rotation": 0.17, "width": 60, "height": 64 } + }, + "hoverboard-thruster-rear": { + "hoverboard-thruster": { "x": 1.1, "y": -6.29, "rotation": 0.17, "width": 60, "height": 64 } + }, + "hoverglow-front": { + "hoverglow-small": { + "x": 2.13, + "y": -2, + "scaleX": 0.303, + "scaleY": 0.495, + "rotation": 0.15, + "width": 274, + "height": 75 + } + }, + "hoverglow-rear": { + "hoverglow-small": { + "x": 1.39, + "y": -2.09, + "scaleX": 0.303, + "scaleY": 0.495, + "rotation": 0.61, + "width": 274, + "height": 75 + } + }, + "mouth": { + "mouth-grind": { + "type": "mesh", + "uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ], + "triangles": [ 1, 3, 0, 1, 2, 3 ], + "vertices": [ 2, 66, -98.93, -85.88, 0.22, 46, 11.28, -85.88, 0.78, 2, 66, -129.77, 1.84, 0.6, 46, -19.56, 1.84, 0.4, 2, 66, -74.12, 21.41, 0.6, 46, 36.09, 21.41, 0.4, 2, 66, -43.28, -66.32, 0.4, 46, 66.93, -66.32, 0.6 ], + "hull": 4, + "edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ], + "width": 93, + "height": 59 + }, + "mouth-oooo": { + "type": "mesh", + "uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ], + "triangles": [ 1, 3, 0, 1, 2, 3 ], + "vertices": [ 2, 46, 11.28, -85.89, 0.22, 66, -98.93, -85.89, 0.78, 2, 46, -19.56, 1.85, 0.6, 66, -129.78, 1.85, 0.4, 2, 46, 36.1, 21.42, 0.6, 66, -74.12, 21.42, 0.4, 2, 46, 66.94, -66.32, 0.4, 66, -43.27, -66.32, 0.6 ], + "hull": 4, + "edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ], + "width": 93, + "height": 59 + }, + "mouth-smile": { + "type": "mesh", + "uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ], + "triangles": [ 1, 3, 0, 1, 2, 3 ], + "vertices": [ 2, 66, -98.93, -85.89, 0.21075, 46, 11.28, -85.89, 0.78925, 2, 66, -129.77, 1.85, 0.6, 46, -19.56, 1.85, 0.4, 2, 66, -74.11, 21.42, 0.6, 46, 36.1, 21.42, 0.4, 2, 66, -43.27, -66.32, 0.40772, 46, 66.94, -66.32, 0.59228 ], + "hull": 4, + "edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ], + "width": 93, + "height": 59 + } + }, + "muzzle": { + "muzzle01": { + "x": 151.97, + "y": 5.81, + "scaleX": 3.7361, + "scaleY": 3.7361, + "rotation": 0.15, + "width": 133, + "height": 79 + }, + "muzzle02": { + "x": 187.25, + "y": 5.9, + "scaleX": 4.0623, + "scaleY": 4.0623, + "rotation": 0.15, + "width": 135, + "height": 84 + }, + "muzzle03": { + "x": 231.96, + "y": 6.02, + "scaleX": 4.1325, + "scaleY": 4.1325, + "rotation": 0.15, + "width": 166, + "height": 106 + }, + "muzzle04": { + "x": 231.96, + "y": 6.02, + "scaleX": 4.0046, + "scaleY": 4.0046, + "rotation": 0.15, + "width": 149, + "height": 90 + }, + "muzzle05": { + "x": 293.8, + "y": 6.19, + "scaleX": 4.4673, + "scaleY": 4.4673, + "rotation": 0.15, + "width": 135, + "height": 75 + } + }, + "muzzle-glow": { + "muzzle-glow": { "width": 50, "height": 50 } + }, + "muzzle-ring": { + "muzzle-ring": { "x": -1.3, "y": 0.32, "scaleX": 0.3147, "scaleY": 0.3147, "width": 49, "height": 209 } + }, + "muzzle-ring2": { + "muzzle-ring": { "x": -1.3, "y": 0.32, "scaleX": 0.3147, "scaleY": 0.3147, "width": 49, "height": 209 } + }, + "muzzle-ring3": { + "muzzle-ring": { "x": -1.3, "y": 0.32, "scaleX": 0.3147, "scaleY": 0.3147, "width": 49, "height": 209 } + }, + "muzzle-ring4": { + "muzzle-ring": { "x": -1.3, "y": 0.32, "scaleX": 0.3147, "scaleY": 0.3147, "width": 49, "height": 209 } + }, + "neck": { + "neck": { "x": 9.77, "y": -3.01, "rotation": -55.22, "width": 36, "height": 41 } + }, + "portal-bg": { + "portal-bg": { "x": -3.1, "y": 7.25, "scaleX": 1.0492, "scaleY": 1.0492, "width": 266, "height": 266 } + }, + "portal-flare1": { + "portal-flare1": { "width": 111, "height": 60 }, + "portal-flare2": { "width": 114, "height": 61 }, + "portal-flare3": { "width": 115, "height": 59 } + }, + "portal-flare2": { + "portal-flare1": { "width": 111, "height": 60 }, + "portal-flare2": { "width": 114, "height": 61 }, + "portal-flare3": { "width": 115, "height": 59 } + }, + "portal-flare3": { + "portal-flare1": { "width": 111, "height": 60 }, + "portal-flare2": { "width": 114, "height": 61 }, + "portal-flare3": { "width": 115, "height": 59 } + }, + "portal-flare4": { + "portal-flare1": { "width": 111, "height": 60 }, + "portal-flare2": { "width": 114, "height": 61 }, + "portal-flare3": { "width": 115, "height": 59 } + }, + "portal-flare5": { + "portal-flare1": { "width": 111, "height": 60 }, + "portal-flare2": { "width": 114, "height": 61 }, + "portal-flare3": { "width": 115, "height": 59 } + }, + "portal-flare6": { + "portal-flare1": { "width": 111, "height": 60 }, + "portal-flare2": { "width": 114, "height": 61 }, + "portal-flare3": { "width": 115, "height": 59 } + }, + "portal-flare7": { + "portal-flare1": { "width": 111, "height": 60 }, + "portal-flare2": { "width": 114, "height": 61 }, + "portal-flare3": { "width": 115, "height": 59 } + }, + "portal-flare8": { + "portal-flare1": { "width": 111, "height": 60 }, + "portal-flare2": { "width": 114, "height": 61 }, + "portal-flare3": { "width": 115, "height": 59 } + }, + "portal-flare9": { + "portal-flare1": { "width": 111, "height": 60 }, + "portal-flare2": { "width": 114, "height": 61 }, + "portal-flare3": { "width": 115, "height": 59 } + }, + "portal-flare10": { + "portal-flare1": { "width": 111, "height": 60 }, + "portal-flare2": { "width": 114, "height": 61 }, + "portal-flare3": { "width": 115, "height": 59 } + }, + "portal-shade": { + "portal-shade": { "width": 266, "height": 266 } + }, + "portal-streaks1": { + "portal-streaks1": { "scaleX": 0.9774, "scaleY": 0.9774, "width": 252, "height": 256 } + }, + "portal-streaks2": { + "portal-streaks2": { "x": -1.64, "y": 2.79, "width": 250, "height": 249 } + }, + "rear-bracer": { + "rear-bracer": { "x": 11.15, "y": -2.2, "rotation": 66.17, "width": 56, "height": 72 } + }, + "rear-foot": { + "rear-foot": { + "type": "mesh", + "uvs": [ 0.48368, 0.1387, 0.51991, 0.21424, 0.551, 0.27907, 0.58838, 0.29816, 0.63489, 0.32191, 0.77342, 0.39267, 1, 0.73347, 1, 1, 0.54831, 0.99883, 0.31161, 1, 0, 1, 0, 0.41397, 0.13631, 0, 0.41717, 0 ], + "triangles": [ 8, 3, 4, 8, 4, 5, 8, 5, 6, 8, 6, 7, 11, 1, 10, 3, 9, 2, 2, 10, 1, 12, 13, 0, 0, 11, 12, 1, 11, 0, 2, 9, 10, 3, 8, 9 ], + "vertices": [ 2, 8, 10.45, 29.41, 0.90802, 9, -6.74, 49.62, 0.09198, 2, 8, 16.56, 29.27, 0.84259, 9, -2.65, 45.09, 0.15741, 2, 8, 21.8, 29.15, 0.69807, 9, 0.85, 41.2, 0.30193, 2, 8, 25.53, 31.43, 0.52955, 9, 5.08, 40.05, 0.47045, 2, 8, 30.18, 34.27, 0.39303, 9, 10.33, 38.62, 0.60697, 2, 8, 44.02, 42.73, 0.27525, 9, 25.98, 34.36, 0.72475, 2, 8, 76.47, 47.28, 0.21597, 9, 51.56, 13.9, 0.78403, 2, 8, 88.09, 36.29, 0.28719, 9, 51.55, -2.09, 0.71281, 2, 8, 52.94, -0.73, 0.47576, 9, 0.52, -1.98, 0.52424, 2, 8, 34.63, -20.23, 0.68757, 9, -26.23, -2.03, 0.31243, 2, 8, 10.44, -45.81, 0.84141, 9, -61.43, -2, 0.15859, 2, 8, -15.11, -21.64, 0.93283, 9, -61.4, 33.15, 0.06717, 1, 8, -22.57, 6.61, 1, 1, 8, -0.76, 29.67, 1 ], + "hull": 14, + "edges": [ 14, 12, 10, 12, 14, 16, 16, 18, 18, 20, 4, 18, 20, 22, 24, 26, 22, 24, 4, 2, 2, 20, 4, 6, 6, 16, 6, 8, 8, 10, 2, 0, 0, 26 ], + "width": 113, + "height": 60 + } + }, + "rear-shin": { + "rear-shin": { "x": 58.29, "y": -2.75, "rotation": 92.37, "width": 75, "height": 178 } + }, + "rear-thigh": { + "rear-thigh": { "x": 33.11, "y": -4.11, "rotation": 72.54, "width": 55, "height": 94 } + }, + "rear-upper-arm": { + "rear-upper-arm": { "x": 21.13, "y": 4.09, "rotation": 89.33, "width": 40, "height": 87 } + }, + "side-glow1": { + "hoverglow-small": { "x": 2.09, "scaleX": 0.2353, "scaleY": 0.4132, "width": 274, "height": 75 } + }, + "side-glow2": { + "hoverglow-small": { "x": 2.09, "scaleX": 0.2353, "scaleY": 0.4132, "width": 274, "height": 75 } + }, + "side-glow3": { + "hoverglow-small": { "x": 2.09, "scaleX": 0.3586, "scaleY": 0.6297, "width": 274, "height": 75 } + }, + "torso": { + "torso": { + "type": "mesh", + "uvs": [ 0.6251, 0.12672, 1, 0.26361, 1, 0.28871, 1, 0.66021, 1, 0.68245, 0.92324, 0.69259, 0.95116, 0.84965, 0.77124, 1, 0.49655, 1, 0.27181, 1, 0.13842, 0.77196, 0.09886, 0.6817, 0.05635, 0.58471, 0, 0.45614, 0, 0.33778, 0, 0.19436, 0.14463, 0, 0.27802, 0, 0.72525, 0.27835, 0.76091, 0.46216, 0.84888, 0.67963, 0.68257, 0.63249, 0.53986, 0.3847, 0.25443, 0.3217, 0.30063, 0.55174, 0.39553, 0.79507, 0.26389, 0.17007, 0.5241, 0.18674, 0.71492, 0.76655, 0.82151, 0.72956, 0.27626, 0.4304, 0.62327, 0.52952, 0.3455, 0.66679, 0.53243, 0.2914 ], + "triangles": [ 18, 1, 2, 19, 2, 3, 18, 0, 1, 23, 15, 26, 27, 26, 16, 14, 15, 23, 15, 16, 26, 17, 27, 16, 13, 14, 23, 0, 27, 17, 13, 23, 30, 11, 12, 24, 21, 31, 19, 12, 13, 30, 24, 22, 31, 31, 22, 19, 12, 30, 24, 32, 24, 31, 24, 30, 22, 3, 20, 19, 32, 31, 21, 11, 24, 32, 4, 5, 3, 8, 28, 7, 7, 29, 6, 7, 28, 29, 9, 25, 8, 8, 25, 28, 9, 10, 25, 29, 5, 6, 10, 32, 25, 25, 21, 28, 25, 32, 21, 10, 11, 32, 28, 21, 29, 29, 20, 5, 29, 21, 20, 5, 20, 3, 20, 21, 19, 33, 26, 27, 22, 18, 19, 19, 18, 2, 33, 27, 18, 30, 23, 22, 22, 33, 18, 23, 33, 22, 33, 23, 26, 27, 0, 18 ], + "vertices": [ 2, 29, 44.59, -10.39, 0.88, 40, -17.65, 33.99, 0.12, 3, 28, 59.65, -45.08, 0.12189, 29, 17.13, -45.08, 0.26811, 40, 22.68, 15.82, 0.61, 3, 28, 55.15, -44.72, 0.1345, 29, 12.63, -44.72, 0.2555, 40, 23.43, 11.37, 0.61, 3, 27, 31.01, -39.45, 0.51133, 28, -11.51, -39.45, 0.30867, 40, 34.58, -54.57, 0.18, 3, 27, 27.01, -39.14, 0.53492, 28, -15.5, -39.14, 0.28508, 40, 35.25, -58.52, 0.18, 2, 27, 25.79, -31.5, 0.75532, 28, -16.73, -31.5, 0.24468, 1, 27, -2.61, -32, 1, 1, 27, -28.2, -12.29, 1, 1, 27, -26.08, 14.55, 1, 1, 27, -24.35, 36.5, 1, 2, 27, 17.6, 46.3, 0.8332, 28, -24.92, 46.3, 0.1668, 2, 27, 34.1, 48.89, 0.59943, 28, -8.42, 48.89, 0.40058, 3, 27, 51.83, 51.67, 0.29262, 28, 9.32, 51.67, 0.63181, 29, -33.2, 51.67, 0.07557, 3, 27, 75.34, 55.35, 0.06656, 28, 32.82, 55.35, 0.62298, 29, -9.7, 55.35, 0.31046, 2, 28, 54.06, 53.67, 0.37296, 29, 11.54, 53.67, 0.62704, 2, 28, 79.79, 51.64, 0.10373, 29, 37.27, 51.64, 0.89627, 1, 29, 71.04, 34.76, 1, 1, 29, 70.01, 21.72, 1, 1, 30, 36.74, 7.06, 1, 3, 30, 45.7, -24.98, 0.67, 28, 25.87, -18.9, 0.3012, 29, -16.65, -18.9, 0.0288, 2, 27, 28.69, -24.42, 0.77602, 28, -13.83, -24.42, 0.22398, 3, 30, 43.24, -56.49, 0.064, 27, 38.43, -8.84, 0.67897, 28, -4.09, -8.84, 0.25703, 3, 30, 22.02, -14.85, 0.29, 28, 41.48, 1.59, 0.53368, 29, -1.04, 1.59, 0.17632, 3, 30, -7.45, -8.33, 0.76, 28, 54.98, 28.59, 0.06693, 29, 12.46, 28.59, 0.17307, 3, 30, 3.91, -48.4, 0.25, 27, 55.87, 27.33, 0.15843, 28, 13.35, 27.33, 0.59157, 1, 27, 11.47, 21.51, 1, 2, 30, -11.09, 18.74, 0.416, 29, 39.6, 25.51, 0.584, 2, 30, 14.56, 20.03, 0.53, 29, 34.6, 0.33, 0.47, 1, 27, 14.12, -10.1, 1, 2, 27, 19.94, -21.03, 0.92029, 28, -22.58, -21.03, 0.07971, 3, 30, -2.08, -27.26, 0.29, 28, 35.31, 27.99, 0.49582, 29, -7.21, 27.99, 0.21418, 2, 30, 34.42, -39.19, 0.25, 28, 14.84, -4.5, 0.75, 2, 27, 34.87, 24.58, 0.67349, 28, -7.64, 24.58, 0.32651, 2, 30, 18.5, 1.59, 0.76, 29, 15.76, 1, 0.24 ], + "hull": 18, + "edges": [ 14, 12, 12, 10, 10, 8, 18, 20, 32, 34, 30, 32, 2, 4, 36, 4, 36, 38, 38, 40, 4, 6, 6, 8, 40, 6, 40, 42, 14, 16, 16, 18, 50, 16, 46, 52, 54, 36, 2, 0, 0, 34, 54, 0, 54, 32, 20, 50, 14, 56, 56, 42, 50, 56, 56, 58, 58, 40, 58, 10, 46, 60, 60, 48, 26, 60, 60, 44, 24, 26, 24, 48, 42, 62, 62, 44, 48, 62, 48, 64, 64, 50, 42, 64, 20, 22, 22, 24, 64, 22, 26, 28, 28, 30, 28, 46, 44, 66, 66, 54, 46, 66, 66, 36, 62, 38 ], + "width": 98, + "height": 180 + } + } + } + } +], +"events": { + "footstep": {} +}, +"animations": { + "aim": { + "slots": { + "crosshair": { + "attachment": [ + { "name": "crosshair" } + ] + } + }, + "bones": { + "front-fist": { + "rotate": [ + { "value": 36.08 } + ] + }, + "rear-bracer": { + "rotate": [ + { "value": -26.55 } + ] + }, + "rear-upper-arm": { + "rotate": [ + { "value": 62.31 } + ] + }, + "front-bracer": { + "rotate": [ + { "value": 9.11 } + ] + }, + "gun": { + "rotate": [ + { "value": -0.31 } + ] + } + }, + "ik": { + "aim-ik": [ + { "mix": 0.995 } + ] + }, + "transform": { + "aim-front-arm-transform": [ + { "mixRotate": 0.784, "mixX": 0, "mixScaleX": 0, "mixShearY": 0 } + ], + "aim-head-transform": [ + { "mixRotate": 0.659, "mixX": 0, "mixScaleX": 0, "mixShearY": 0 } + ], + "aim-torso-transform": [ + { "mixRotate": 0.423, "mixX": 0, "mixScaleX": 0, "mixShearY": 0 } + ] + } + }, + "death": { + "slots": { + "eye": { + "attachment": [ + { "name": "eye-surprised" }, + { "time": 0.5333, "name": "eye-indifferent" }, + { "time": 2.2, "name": "eye-surprised" }, + { "time": 4.6, "name": "eye-indifferent" } + ] + }, + "front-fist": { + "attachment": [ + { "name": "front-fist-open" } + ] + }, + "mouth": { + "attachment": [ + { "name": "mouth-oooo" }, + { "time": 0.5333, "name": "mouth-grind" }, + { "time": 1.4, "name": "mouth-oooo" }, + { "time": 2.1667, "name": "mouth-grind" }, + { "time": 4.5333, "name": "mouth-oooo" } + ] + } + }, + "bones": { + "head": { + "rotate": [ + { + "value": -2.83, + "curve": [ 0.015, -2.83, 0.036, 12.72 ] + }, + { + "time": 0.0667, + "value": 12.19, + "curve": [ 0.096, 11.68, 0.119, -1.14 ] + }, + { + "time": 0.1333, + "value": -6.86, + "curve": [ 0.149, -13.27, 0.21, -37.28 ] + }, + { + "time": 0.3, + "value": -36.86, + "curve": [ 0.354, -36.61, 0.412, -32.35 ] + }, + { + "time": 0.4667, + "value": -23.49, + "curve": [ 0.49, -19.87, 0.512, -3.29 ] + }, + { + "time": 0.5333, + "value": -3.24, + "curve": [ 0.56, -3.39, 0.614, -67.25 ] + }, + { + "time": 0.6333, + "value": -74.4, + "curve": [ 0.652, -81.58, 0.702, -88.94 ] + }, + { + "time": 0.7333, + "value": -88.93, + "curve": [ 0.805, -88.91, 0.838, -80.87 ] + }, + { + "time": 0.8667, + "value": -81.03, + "curve": [ 0.922, -81.32, 0.976, -85.29 ] + }, + { "time": 1, "value": -85.29, "curve": "stepped" }, + { + "time": 2.2333, + "value": -85.29, + "curve": [ 2.314, -85.29, 2.382, -68.06 ] + }, + { + "time": 2.4667, + "value": -63.48, + "curve": [ 2.57, -57.87, 2.916, -55.24 ] + }, + { + "time": 3.2, + "value": -55.1, + "curve": [ 3.447, -54.98, 4.135, -56.61 ] + }, + { + "time": 4.2667, + "value": -58.23, + "curve": [ 4.672, -63.24, 4.646, -82.69 ] + }, + { "time": 4.9333, "value": -85.29 } + ], + "scale": [ + { + "time": 0.4667, + "curve": [ 0.469, 1.005, 0.492, 1.065, 0.475, 1.018, 0.492, 0.94 ] + }, + { + "time": 0.5, + "x": 1.065, + "y": 0.94, + "curve": [ 0.517, 1.065, 0.541, 0.991, 0.517, 0.94, 0.542, 1.026 ] + }, + { + "time": 0.5667, + "x": 0.99, + "y": 1.025, + "curve": [ 0.593, 0.988, 0.609, 1.002, 0.595, 1.024, 0.607, 1.001 ] + }, + { "time": 0.6333 } + ] + }, + "neck": { + "rotate": [ + { + "value": -2.83, + "curve": [ 0.114, 1.33, 0.195, 4.13 ] + }, + { + "time": 0.2667, + "value": 4.13, + "curve": [ 0.351, 4.14, 0.444, -24.5 ] + }, + { + "time": 0.5, + "value": -24.69, + "curve": [ 0.571, -23.89, 0.55, 34.22 ] + }, + { + "time": 0.6667, + "value": 35.13, + "curve": [ 0.713, 34.81, 0.756, 22.76 ] + }, + { + "time": 0.8333, + "value": 22.82, + "curve": [ 0.868, 22.84, 0.916, 47.95 ] + }, + { "time": 0.9667, "value": 47.95, "curve": "stepped" }, + { + "time": 2.2333, + "value": 47.95, + "curve": [ 2.3, 47.95, 2.617, 18.72 ] + }, + { + "time": 2.6667, + "value": 18.51, + "curve": [ 3.172, 16.58, 4.06, 16.79 ] + }, + { + "time": 4.5333, + "value": 18.51, + "curve": [ 4.707, 19.13, 4.776, 41.11 ] + }, + { "time": 4.8, "value": 47.95 } + ] + }, + "torso": { + "rotate": [ + { + "value": -8.62, + "curve": [ 0.01, -16.71, 0.032, -33.6 ] + }, + { + "time": 0.0667, + "value": -33.37, + "curve": [ 0.182, -32.61, 0.298, 123.07 ] + }, + { + "time": 0.4667, + "value": 122.77, + "curve": [ 0.511, 122.69, 0.52, 100.2 ] + }, + { + "time": 0.5667, + "value": 88.96, + "curve": [ 0.588, 83.89, 0.667, 75.34 ] + }, + { + "time": 0.7, + "value": 75.34, + "curve": [ 0.767, 75.34, 0.9, 76.03 ] + }, + { "time": 0.9667, "value": 76.03 } + ] + }, + "front-upper-arm": { + "rotate": [ + { + "value": -38.86, + "curve": [ 0.022, -40.38, 0.096, -41.92 ] + }, + { + "time": 0.1333, + "value": -41.92, + "curve": [ 0.176, -41.92, 0.216, -16.92 ] + }, + { + "time": 0.2333, + "value": -4.35, + "curve": [ 0.258, 13.69, 0.308, 60.35 ] + }, + { + "time": 0.4, + "value": 60.17, + "curve": [ 0.496, 59.98, 0.539, 33.63 ] + }, + { + "time": 0.5667, + "value": 23.06, + "curve": [ 0.595, 32.71, 0.675, 53.71 ] + }, + { + "time": 0.7333, + "value": 53.61, + "curve": [ 0.797, 53.51, 0.926, 30.98 ] + }, + { "time": 0.9333, "value": 19.57, "curve": "stepped" }, + { + "time": 1.9667, + "value": 19.57, + "curve": [ 2.245, 19.57, 2.702, 77.03 ] + }, + { + "time": 3.0667, + "value": 77.06, + "curve": [ 3.209, 77.33, 3.291, 67.99 ] + }, + { + "time": 3.4333, + "value": 67.96, + "curve": [ 3.608, 68.34, 3.729, 73.88 ] + }, + { + "time": 3.8333, + "value": 73.42, + "curve": [ 4.152, 73.91, 4.46, 71.98 ] + }, + { + "time": 4.6333, + "value": 64.77, + "curve": [ 4.688, 62.5, 4.847, 26.42 ] + }, + { "time": 4.8667, "value": 10.94 } + ] + }, + "rear-upper-arm": { + "rotate": [ + { + "value": -44.7, + "curve": [ 0.033, -44.7, 0.12, 54.89 ] + }, + { + "time": 0.1333, + "value": 64.62, + "curve": [ 0.154, 79.18, 0.214, 79.42 ] + }, + { + "time": 0.2667, + "value": 63.4, + "curve": [ 0.293, 55.19, 0.332, 30.13 ] + }, + { + "time": 0.3667, + "value": 30.13, + "curve": [ 0.4, 30.13, 0.441, 39.87 ] + }, + { + "time": 0.4667, + "value": 55.13, + "curve": [ 0.488, 68.18, 0.52, 100.72 ] + }, + { + "time": 0.5333, + "value": 111.96, + "curve": [ 0.551, 126.88, 0.627, 185.97 ] + }, + { + "time": 0.6667, + "value": 185.97, + "curve": [ 0.692, 185.97, 0.736, 162.43 ] + }, + { + "time": 0.8, + "value": 158.01, + "curve": [ 0.9, 151.12, 1.017, 144.01 ] + }, + { "time": 1.1, "value": 144.01, "curve": "stepped" }, + { + "time": 2.3667, + "value": 144.01, + "curve": [ 2.492, 144.01, 2.742, 138.63 ] + }, + { + "time": 2.8667, + "value": 138.63, + "curve": [ 3.067, 138.63, 3.467, 138.63 ] + }, + { + "time": 3.6667, + "value": 138.63, + "curve": [ 3.883, 138.63, 4.317, 135.18 ] + }, + { + "time": 4.5333, + "value": 135.18, + "curve": [ 4.575, 135.18, 4.692, 131.59 ] + }, + { + "time": 4.7333, + "value": 131.59, + "curve": [ 4.758, 131.59, 4.517, 144.01 ] + }, + { "time": 4.8333, "value": 144.01 } + ], + "translate": [ + { + "time": 0.4667, + "curve": [ 0.517, 0, 0.617, -34.96, 0.517, 0, 0.617, -16.59 ] + }, + { "time": 0.6667, "x": -35.02, "y": -16.62 } + ] + }, + "front-bracer": { + "rotate": [ + { + "value": 21.88, + "curve": [ 0.033, 21.88, 0.099, 20.44 ] + }, + { + "time": 0.1333, + "value": 9.43, + "curve": [ 0.164, -0.29, 0.162, -38.26 ] + }, + { + "time": 0.2, + "value": -38.05, + "curve": [ 0.24, -37.96, 0.228, -17.82 ] + }, + { + "time": 0.3333, + "value": -9.73, + "curve": [ 0.372, -6.76, 0.431, -0.74 ] + }, + { + "time": 0.4667, + "value": 6.47, + "curve": [ 0.489, 11.05, 0.503, 19.09 ] + }, + { + "time": 0.5333, + "value": 19.09, + "curve": [ 0.571, 19.09, 0.554, -42.67 ] + }, + { + "time": 0.6, + "value": -42.67, + "curve": [ 0.653, -42.67, 0.691, -13.8 ] + }, + { + "time": 0.7, + "value": -3.54, + "curve": [ 0.707, 3.8, 0.719, 24.94 ] + }, + { + "time": 0.8, + "value": 25.31, + "curve": [ 0.902, 24.75, 0.992, -0.34 ] + }, + { "time": 1, "value": -32.16, "curve": "stepped" }, + { + "time": 2.2333, + "value": -32.16, + "curve": [ 2.6, -32.16, 2.638, -5.3 ] + }, + { + "time": 2.7, + "value": -1.96, + "curve": [ 2.707, -1.56, 2.775, 1.67 ] + }, + { + "time": 2.8, + "value": 1.67, + "curve": [ 2.825, 1.67, 2.875, -0.39 ] + }, + { + "time": 2.9, + "value": -0.39, + "curve": [ 2.925, -0.39, 2.975, 0.26 ] + }, + { + "time": 3, + "value": 0.26, + "curve": [ 3.025, 0.26, 3.075, -1.81 ] + }, + { + "time": 3.1, + "value": -1.81, + "curve": [ 3.125, -1.81, 3.175, -0.52 ] + }, + { + "time": 3.2, + "value": -0.52, + "curve": [ 3.225, -0.52, 3.275, -2.41 ] + }, + { + "time": 3.3, + "value": -2.41, + "curve": [ 3.333, -2.41, 3.4, -0.38 ] + }, + { + "time": 3.4333, + "value": -0.38, + "curve": [ 3.467, -0.38, 3.533, -2.25 ] + }, + { + "time": 3.5667, + "value": -2.25, + "curve": [ 3.592, -2.25, 3.642, -0.33 ] + }, + { + "time": 3.6667, + "value": -0.33, + "curve": [ 3.7, -0.33, 3.767, -1.34 ] + }, + { + "time": 3.8, + "value": -1.34, + "curve": [ 3.825, -1.34, 3.862, -0.77 ] + }, + { + "time": 3.9, + "value": -0.77, + "curve": [ 3.942, -0.77, 3.991, -1.48 ] + }, + { + "time": 4, + "value": -1.87, + "curve": [ 4.167, -1.87, 4.5, -1.96 ] + }, + { + "time": 4.6667, + "value": -1.96, + "curve": [ 4.709, 18.05, 4.767, 34.55 ] + }, + { + "time": 4.8, + "value": 34.55, + "curve": [ 4.84, 34.24, 4.902, 12.03 ] + }, + { "time": 4.9333, "value": -18.75 } + ] + }, + "front-fist": { + "rotate": [ + { + "value": -2.33, + "curve": [ 0.019, 4.43, 0.069, 10.82 ] + }, + { + "time": 0.1, + "value": 10.6, + "curve": [ 0.148, 10.6, 0.123, -15.24 ] + }, + { + "time": 0.2, + "value": -15.35, + "curve": [ 0.266, -15.44, 0.316, -6.48 ] + }, + { + "time": 0.3333, + "value": -3.9, + "curve": [ 0.362, 0.43, 0.479, 22.36 ] + }, + { + "time": 0.5667, + "value": 22.01, + "curve": [ 0.61, 21.84, 0.627, 12.85 ] + }, + { + "time": 0.6333, + "value": 9.05, + "curve": [ 0.643, 2.77, 0.622, -39.43 ] + }, + { + "time": 0.7, + "value": -39.5, + "curve": [ 0.773, -39.57, 0.814, 14.77 ] + }, + { + "time": 0.8667, + "value": 14.81, + "curve": [ 0.965, 14.88, 1.1, 5.64 ] + }, + { "time": 1.1, "value": -6.08, "curve": "stepped" }, + { + "time": 2.2333, + "value": -6.08, + "curve": [ 2.307, -6.08, 2.427, -25.89 ] + }, + { + "time": 2.5333, + "value": -22.42, + "curve": [ 2.598, -20.38, 2.657, 5.73 ] + }, + { + "time": 2.7, + "value": 5.73, + "curve": [ 2.77, 5.73, 2.851, -5.38 ] + }, + { + "time": 2.9333, + "value": -5.38, + "curve": [ 3.008, -5.38, 3.087, -4.54 ] + }, + { + "time": 3.1667, + "value": -4.17, + "curve": [ 3.223, -3.91, 4.486, 5.73 ] + }, + { + "time": 4.6667, + "value": 5.73, + "curve": [ 4.733, 5.73, 4.886, -2.47 ] + }, + { "time": 4.9333, "value": -6.52 } + ] + }, + "rear-bracer": { + "rotate": [ + { + "value": 10.36, + "curve": [ 0.033, 10.36, 0.1, -32.89 ] + }, + { + "time": 0.1333, + "value": -32.89, + "curve": [ 0.183, -32.89, 0.283, -4.45 ] + }, + { + "time": 0.3333, + "value": -4.45, + "curve": [ 0.367, -4.45, 0.438, -6.86 ] + }, + { + "time": 0.4667, + "value": -8.99, + "curve": [ 0.529, -13.62, 0.605, -20.58 ] + }, + { + "time": 0.6333, + "value": -23.2, + "curve": [ 0.708, -30.18, 0.758, -35.56 ] + }, + { + "time": 0.8, + "value": -35.56, + "curve": [ 0.875, -35.56, 1.025, -23.2 ] + }, + { "time": 1.1, "value": -23.2 } + ] + }, + "gun": { + "rotate": [ + { + "value": -2.79, + "curve": [ 0.033, -2.79, 0.12, -7.22 ] + }, + { + "time": 0.1333, + "value": -8.52, + "curve": [ 0.168, -11.87, 0.29, -23.71 ] + }, + { + "time": 0.3333, + "value": -26.24, + "curve": [ 0.369, -28.31, 0.436, -29.75 ] + }, + { + "time": 0.5, + "value": -29.66, + "curve": [ 0.552, -29.58, 0.611, -25.47 ] + }, + { + "time": 0.6333, + "value": -22.68, + "curve": [ 0.656, -19.76, 0.68, -10.02 ] + }, + { + "time": 0.7, + "value": -6.49, + "curve": [ 0.722, -2.6, 0.75, -1.22 ] + }, + { + "time": 0.7667, + "value": -1.35, + "curve": [ 0.792, -1.55, 0.842, -19.74 ] + }, + { "time": 0.8667, "value": -19.8 } + ] + }, + "hip": { + "translate": [ + { + "curve": [ 0.098, -42.62, 0.166, -79.85, 0.029, 84.97, 0.109, 155.93 ] + }, + { + "time": 0.2667, + "x": -133.79, + "y": 152.44, + "curve": [ 0.361, -184.63, 0.392, -203.69, 0.42, 149.12, 0.467, -15.7 ] + }, + { + "time": 0.4667, + "x": -230.02, + "y": -113.87, + "curve": [ 0.523, -249.86, 0.565, -261.7, 0.473, -133.1, 0.583, -203.43 ] + }, + { + "time": 0.6, + "x": -268.57, + "y": -203.43, + "curve": [ 0.663, -280.98, 0.816, -290.05, 0.708, -203.43, 0.892, -203.5 ] + }, + { "time": 1, "x": -290.42, "y": -203.5 } + ] + }, + "front-thigh": { + "rotate": [ + { + "curve": [ 0.06, 1.02, 0.151, 45.23 ] + }, + { + "time": 0.1667, + "value": 54.01, + "curve": [ 0.19, 66.85, 0.358, 169.85 ] + }, + { + "time": 0.5, + "value": 169.51, + "curve": [ 0.628, 169.85, 0.692, 108.85 ] + }, + { + "time": 0.7, + "value": 97.74, + "curve": [ 0.723, 102.6, 0.805, 111.6 ] + }, + { + "time": 0.8667, + "value": 111.69, + "curve": [ 0.899, 111.83, 1.015, 109.15 ] + }, + { "time": 1.0667, "value": 95.8 } + ] + }, + "front-shin": { + "rotate": [ + { + "curve": [ 0.086, -0.02, 0.191, -24.25 ] + }, + { + "time": 0.2, + "value": -26.5, + "curve": [ 0.214, -29.92, 0.249, -40.51 ] + }, + { + "time": 0.3333, + "value": -40.57, + "curve": [ 0.431, -40.7, 0.459, -11.34 ] + }, + { + "time": 0.4667, + "value": -8.71, + "curve": [ 0.477, -5.16, 0.524, 17.13 ] + }, + { + "time": 0.6, + "value": 16.98, + "curve": [ 0.632, 17.09, 0.625, 2.76 ] + }, + { + "time": 0.6333, + "value": 2.76, + "curve": [ 0.648, 2.76, 0.653, 2.75 ] + }, + { + "time": 0.6667, + "value": 2.59, + "curve": [ 0.678, 2.39, 0.733, 2.53 ] + }, + { + "time": 0.7333, + "value": -9.43, + "curve": [ 0.745, -2.48, 0.782, 3.12 ] + }, + { + "time": 0.8, + "value": 4.28, + "curve": [ 0.832, 6.32, 0.895, 8.46 ] + }, + { + "time": 0.9333, + "value": 8.49, + "curve": [ 0.986, 8.53, 1.051, 6.38 ] + }, + { + "time": 1.0667, + "value": 2.28, + "curve": [ 1.078, 4.17, 1.103, 5.86 ] + }, + { + "time": 1.1333, + "value": 5.88, + "curve": [ 1.191, 5.93, 1.209, 4.56 ] + }, + { "time": 1.2333, "value": 2.52 } + ] + }, + "rear-thigh": { + "rotate": [ + { + "curve": [ 0.033, 0, 0.12, 50.26 ] + }, + { + "time": 0.1333, + "value": 57.3, + "curve": [ 0.164, 73.34, 0.274, 147.18 ] + }, + { + "time": 0.3333, + "value": 147.1, + "curve": [ 0.475, 146.45, 0.583, 95.72 ] + }, + { + "time": 0.6, + "value": 79.66, + "curve": [ 0.62, 94.74, 0.732, 103.15 ] + }, + { + "time": 0.7667, + "value": 103.02, + "curve": [ 0.812, 102.85, 0.897, 95.75 ] + }, + { "time": 0.9333, "value": 83.01 } + ] + }, + "rear-shin": { + "rotate": [ + { + "curve": [ 0.021, -16.65, 0.091, -54.82 ] + }, + { + "time": 0.1667, + "value": -55.29, + "curve": [ 0.187, -55.42, 0.213, -52.52 ] + }, + { + "time": 0.2333, + "value": -45.98, + "curve": [ 0.242, -43.1, 0.311, -12.73 ] + }, + { + "time": 0.3333, + "value": -6.32, + "curve": [ 0.356, 0.13, 0.467, 24.5 ] + }, + { + "time": 0.5, + "value": 24.5, + "curve": [ 0.543, 24.5, 0.56, 3.78 ] + }, + { + "time": 0.5667, + "value": -3.53, + "curve": [ 0.585, 3.86, 0.659, 16.63 ] + }, + { + "time": 0.7, + "value": 16.56, + "curve": [ 0.782, 16.43, 0.896, 8.44 ] + }, + { + "time": 0.9333, + "value": 4.04, + "curve": [ 0.956, 6.84, 1.008, 8.41 ] + }, + { + "time": 1.0333, + "value": 8.41, + "curve": [ 1.067, 8.41, 1.122, 8.14 ] + }, + { "time": 1.1667, "value": 5.8 } + ] + }, + "rear-foot": { + "rotate": [ + { + "value": -0.28, + "curve": [ 0.033, -0.28, 0.256, -66.71 ] + }, + { + "time": 0.3667, + "value": -66.84, + "curve": [ 0.418, -66.91, 0.499, -21.79 ] + }, + { + "time": 0.6, + "value": -21.52, + "curve": [ 0.652, -21.38, 0.665, -53.96 ] + }, + { + "time": 0.7, + "value": -54.26, + "curve": [ 0.757, -53.96, 0.843, -2.07 ] + }, + { + "time": 0.9333, + "value": -1.47, + "curve": [ 0.968, -2.07, 0.975, -19.96 ] + }, + { + "time": 1, + "value": -19.96, + "curve": [ 1.025, -19.96, 1.075, -12.42 ] + }, + { + "time": 1.1, + "value": -12.42, + "curve": [ 1.133, -12.42, 1.2, -18.34 ] + }, + { "time": 1.2333, "value": -18.34 } + ] + }, + "front-foot": { + "rotate": [ + { + "curve": [ 0.008, -11.33, 0.108, -57.71 ] + }, + { + "time": 0.1333, + "value": -57.71, + "curve": [ 0.175, -57.71, 0.229, 19.73 ] + }, + { + "time": 0.3, + "value": 19.34, + "curve": [ 0.354, 19.34, 0.4, -57.76 ] + }, + { + "time": 0.4333, + "value": -57.76, + "curve": [ 0.458, -57.76, 0.511, -3.56 ] + }, + { + "time": 0.5333, + "value": 3.7, + "curve": [ 0.563, 13.29, 0.633, 15.79 ] + }, + { + "time": 0.6667, + "value": 15.79, + "curve": [ 0.7, 15.79, 0.767, -48.75 ] + }, + { + "time": 0.8, + "value": -48.75, + "curve": [ 0.842, -48.75, 0.925, 4.7 ] + }, + { + "time": 0.9667, + "value": 4.7, + "curve": [ 1, 4.7, 1.067, -22.9 ] + }, + { + "time": 1.1, + "value": -22.9, + "curve": [ 1.142, -22.9, 1.225, -13.28 ] + }, + { "time": 1.2667, "value": -13.28 } + ] + }, + "rear-foot-target": { + "rotate": [ + { "value": -0.28 } + ] + }, + "front-foot-tip": { + "rotate": [ + { + "value": -0.28, + "curve": [ 0.008, -0.28, 0.003, -66.62 ] + }, + { + "time": 0.0667, + "value": -65.75, + "curve": [ 0.166, -64.42, 0.234, 14.35 ] + }, + { + "time": 0.2667, + "value": 38.25, + "curve": [ 0.294, 57.91, 0.392, 89.79 ] + }, + { + "time": 0.4667, + "value": 90.73, + "curve": [ 0.483, 90.73, 0.55, 177.66 ] + }, + { + "time": 0.5667, + "value": 177.66, + "curve": [ 0.733, 176.24, 0.75, 11.35 ] + }, + { + "time": 0.8, + "value": 11.35, + "curve": [ 0.886, 12.29, 0.911, 47.88 ] + }, + { + "time": 0.9333, + "value": 56.77, + "curve": [ 0.967, 70.59, 1.05, 86.46 ] + }, + { + "time": 1.1, + "value": 86.46, + "curve": [ 1.187, 86.46, 1.214, 66.44 ] + }, + { "time": 1.3333, "value": 64.55 } + ] + }, + "back-foot-tip": { + "rotate": [ + { + "value": -0.28, + "curve": [ 0, -7.97, 0.027, -18.69 ] + }, + { + "time": 0.0667, + "value": -19, + "curve": [ 0.166, -19.3, 0.208, 15.58 ] + }, + { + "time": 0.2667, + "value": 45.95, + "curve": [ 0.306, 66.24, 0.378, 99.08 ] + }, + { + "time": 0.4333, + "value": 99.08, + "curve": [ 0.497, 98.62, 0.488, -1.2 ] + }, + { + "time": 0.5667, + "value": -1.32, + "curve": [ 0.637, -0.84, 0.687, 94.41 ] + }, + { + "time": 0.7333, + "value": 94.33, + "curve": [ 0.832, 94.16, 0.895, 29.6 ] + }, + { + "time": 0.9667, + "value": 28.67, + "curve": [ 1.026, 28.67, 1.045, 53.14 ] + }, + { "time": 1.1, "value": 53.38 } + ] + }, + "hair4": { + "rotate": [ + { + "curve": [ 0.011, 4.5, 0.05, 11.42 ] + }, + { + "time": 0.0667, + "value": 11.42, + "curve": [ 0.1, 11.42, 0.136, -5.92 ] + }, + { + "time": 0.1667, + "value": -10.54, + "curve": [ 0.206, -16.51, 0.327, -22 ] + }, + { + "time": 0.3667, + "value": -24.47, + "curve": [ 0.413, -27.37, 0.467, -43.99 ] + }, + { + "time": 0.5, + "value": -43.99, + "curve": [ 0.533, -43.99, 0.552, 12.12 ] + }, + { + "time": 0.6333, + "value": 11.85, + "curve": [ 0.714, 11.59, 0.758, -34.13 ] + }, + { + "time": 0.8, + "value": -34.13, + "curve": [ 0.858, -34.13, 1.015, -12.47 ] + }, + { + "time": 1.0667, + "value": -8.85, + "curve": [ 1.121, -5.07, 1.219, -0.02 ] + }, + { + "time": 1.3333, + "value": 1.29, + "curve": [ 1.509, 3.3, 1.763, 2.75 ] + }, + { + "time": 1.8667, + "value": 2.78, + "curve": [ 1.974, 2.81, 2.108, 2.81 ] + }, + { + "time": 2.2, + "value": 2.78, + "curve": [ 2.315, 2.74, 2.374, 1.22 ] + }, + { + "time": 2.4667, + "value": 1.18, + "curve": [ 2.525, 1.18, 2.608, 10.79 ] + }, + { + "time": 2.6667, + "value": 10.79, + "curve": [ 2.725, 10.79, 2.893, 4.72 ] + }, + { + "time": 3.0333, + "value": 4.72, + "curve": [ 3.117, 4.72, 3.283, 7.93 ] + }, + { + "time": 3.3667, + "value": 7.93, + "curve": [ 3.492, 7.93, 3.775, 6.93 ] + }, + { + "time": 3.9, + "value": 6.93, + "curve": [ 3.981, 6.93, 4.094, 6.9 ] + }, + { + "time": 4.2, + "value": 8.44, + "curve": [ 4.267, 9.42, 4.401, 16.61 ] + }, + { + "time": 4.5, + "value": 16.33, + "curve": [ 4.582, 16.12, 4.709, 9.94 ] + }, + { + "time": 4.7333, + "value": 6.51, + "curve": [ 4.747, 4.57, 4.779, -1.76 ] + }, + { + "time": 4.8, + "value": -1.75, + "curve": [ 4.823, -1.73, 4.82, 4.47 ] + }, + { + "time": 4.8667, + "value": 6.04, + "curve": [ 4.899, 7.14, 4.913, 6.93 ] + }, + { "time": 4.9333, "value": 6.93 } + ] + }, + "hair2": { + "rotate": [ + { + "value": 10.61, + "curve": [ 0.075, 10.61, 0.05, 12.67 ] + }, + { + "time": 0.0667, + "value": 12.67, + "curve": [ 0.123, 12.67, 0.194, -16.51 ] + }, + { + "time": 0.2, + "value": -19.87, + "curve": [ 0.207, -23.48, 0.236, -31.68 ] + }, + { + "time": 0.3, + "value": -31.8, + "curve": [ 0.356, -31.9, 0.437, -25.61 ] + }, + { + "time": 0.4667, + "value": -19.29, + "curve": [ 0.485, -15.33, 0.529, 6.48 ] + }, + { + "time": 0.5667, + "value": 6.67, + "curve": [ 0.628, 6.97, 0.65, -46.39 ] + }, + { + "time": 0.7333, + "value": -46.3, + "curve": [ 0.843, -46.17, 0.941, -33.37 ] + }, + { + "time": 0.9667, + "value": -23.17, + "curve": [ 0.972, -20.98, 1.047, 15.21 ] + }, + { + "time": 1.1, + "value": 15.21, + "curve": [ 1.142, 15.21, 1.183, 10.73 ] + }, + { + "time": 1.2667, + "value": 10.61, + "curve": [ 1.45, 10.34, 1.817, 10.61 ] + }, + { + "time": 2, + "value": 10.61, + "curve": [ 2.075, 10.61, 2.225, 16.9 ] + }, + { + "time": 2.3, + "value": 16.9, + "curve": [ 2.327, 16.9, 2.347, 6.81 ] + }, + { + "time": 2.4, + "value": 6.83, + "curve": [ 2.492, 6.87, 2.602, 17.39 ] + }, + { + "time": 2.6667, + "value": 17.39, + "curve": [ 2.742, 17.39, 2.892, 10.67 ] + }, + { + "time": 2.9667, + "value": 10.64, + "curve": [ 3.187, 10.57, 3.344, 10.73 ] + }, + { + "time": 3.6, + "value": 11.4, + "curve": [ 3.766, 11.83, 3.874, 14.87 ] + }, + { + "time": 3.9333, + "value": 14.83, + "curve": [ 4.022, 14.76, 4.208, 9.49 ] + }, + { + "time": 4.3, + "value": 9.54, + "curve": [ 4.391, 9.58, 4.441, 14.82 ] + }, + { + "time": 4.5333, + "value": 14.84, + "curve": [ 4.642, 14.88, 4.692, 1.17 ] + }, + { + "time": 4.7667, + "value": 1.24, + "curve": [ 4.823, 1.3, 4.818, 18.35 ] + }, + { + "time": 4.8667, + "value": 18.38, + "curve": [ 4.905, 18.41, 4.901, 10.61 ] + }, + { "time": 4.9333, "value": 10.61 } + ] + }, + "torso2": { + "rotate": [ + { + "curve": [ 0.048, 0, 0.129, -12.73 ] + }, + { + "time": 0.1667, + "value": -15.95, + "curve": [ 0.221, -20.66, 0.254, -21.62 ] + }, + { + "time": 0.3, + "value": -21.59, + "curve": [ 0.458, -21.46, 0.46, -1.67 ] + }, + { + "time": 0.6333, + "value": -1.71, + "curve": [ 0.71, -1.73, 0.715, -4 ] + }, + { + "time": 0.7667, + "value": -3.97, + "curve": [ 0.866, -3.92, 0.84, 0.02 ] + }, + { "time": 1, "curve": "stepped" }, + { + "time": 2, + "curve": [ 2.275, 0, 2.867, -5.8 ] + }, + { + "time": 3.1, + "value": -6.44, + "curve": [ 3.327, -7.06, 3.71, -6.23 ] + }, + { + "time": 3.9333, + "value": -5.41, + "curve": [ 4.168, -4.53, 4.488, -2.83 ] + }, + { "time": 4.8 } + ] + }, + "torso3": { + "rotate": [ + { + "curve": [ 0.025, 0, 0.09, -3.66 ] + }, + { + "time": 0.1, + "value": -4.55, + "curve": [ 0.143, -8.4, 0.223, -17.07 ] + }, + { + "time": 0.2333, + "value": -18.31, + "curve": [ 0.282, -24.44, 0.35, -29 ] + }, + { + "time": 0.3667, + "value": -30.07, + "curve": [ 0.405, -32.58, 0.442, -33.03 ] + }, + { + "time": 0.4667, + "value": -32.99, + "curve": [ 0.491, -33.04, 0.505, -23.56 ] + }, + { + "time": 0.5333, + "value": -23.55, + "curve": [ 0.571, -23.67, 0.599, -27.21 ] + }, + { + "time": 0.6333, + "value": -27.21, + "curve": [ 0.669, -27.2, 0.742, -10.43 ] + }, + { + "time": 0.7667, + "value": -7.79, + "curve": [ 0.788, -5.53, 0.796, -4.42 ] + }, + { + "time": 0.8333, + "value": -2.9, + "curve": [ 0.875, -1.21, 0.933, 0 ] + }, + { "time": 0.9667, "curve": "stepped" }, + { + "time": 2.4333, + "curve": [ 2.517, 0, 2.683, 4.63 ] + }, + { + "time": 2.7667, + "value": 4.66, + "curve": [ 3.084, 4.76, 3.248, 4.37 ] + }, + { + "time": 3.4, + "value": 3.74, + "curve": [ 3.596, 2.92, 3.755, 2.18 ] + }, + { + "time": 3.8667, + "value": 1.72, + "curve": [ 4.136, 0.59, 4.471, 0 ] + }, + { "time": 4.8 } + ] + }, + "hair3": { + "rotate": [ + { + "curve": [ 0, 0, 0.041, 10.74 ] + }, + { + "time": 0.0667, + "value": 14.16, + "curve": [ 0.075, 15.22, 0.148, 18.04 ] + }, + { + "time": 0.2, + "value": 18.13, + "curve": [ 0.251, 18.23, 0.307, -4.75 ] + }, + { + "time": 0.3667, + "value": -5.06, + "curve": [ 0.412, -5.3, 0.47, -0.96 ] + }, + { + "time": 0.5, + "value": 2.21, + "curve": [ 0.512, 3.48, 0.595, 20.31 ] + }, + { + "time": 0.6333, + "value": 24.87, + "curve": [ 0.647, 26.53, 0.719, 29.33 ] + }, + { + "time": 0.8, + "value": 29.22, + "curve": [ 0.859, 29.14, 0.9, 28.48 ] + }, + { + "time": 0.9333, + "value": 26.11, + "curve": [ 0.981, 22.72, 0.998, 2.06 ] + }, + { "time": 1.1, "value": 2.21 } + ] + }, + "hair1": { + "rotate": [ + { + "curve": [ 0.047, -0.21, 0.048, 7.86 ] + }, + { + "time": 0.0667, + "value": 13.27, + "curve": [ 0.083, 18.05, 0.135, 24.44 ] + }, + { + "time": 0.2, + "value": 24.02, + "curve": [ 0.225, 24.02, 0.28, 6.32 ] + }, + { + "time": 0.3, + "value": 3.1, + "curve": [ 0.323, -0.58, 0.382, -7.12 ] + }, + { + "time": 0.4667, + "value": -7.45, + "curve": [ 0.512, -7.66, 0.538, 12.13 ] + }, + { + "time": 0.5667, + "value": 16.46, + "curve": [ 0.609, 22.72, 0.672, 27.4 ] + }, + { + "time": 0.7333, + "value": 27.55, + "curve": [ 0.827, 27.4, 0.933, 23.23 ] + }, + { + "time": 0.9667, + "value": 19.11, + "curve": [ 0.998, 15.27, 1.092, -2.53 ] + }, + { + "time": 1.1333, + "value": -2.53, + "curve": [ 1.158, -2.53, 1.208, 0 ] + }, + { "time": 1.2333, "curve": "stepped" }, + { + "time": 2, + "curve": [ 2.075, 0, 2.248, 0.35 ] + }, + { + "time": 2.3333, + "value": 0.78, + "curve": [ 2.585, 2.06, 2.805, 3.46 ] + }, + { + "time": 3.2, + "value": 3.5, + "curve": [ 3.593, 3.54, 3.979, 2.36 ] + }, + { + "time": 4.1667, + "value": 1.55, + "curve": [ 4.391, 0.59, 4.447, 0.04 ] + }, + { + "time": 4.6, + "value": 0.04, + "curve": [ 4.642, 0.04, 4.742, 0 ] + }, + { "time": 4.9333 } + ] + }, + "head-control": { + "translate": [ + { + "curve": [ 0.025, 0, 0.09, 1.43, 0.025, 0, 0.075, -34.76 ] + }, + { + "time": 0.1, + "x": 1.59, + "y": -34.76, + "curve": [ 0.214, 3.33, 0.375, 5.34, 0.192, -34.76, 0.441, -21.17 ] + }, + { + "time": 0.4667, + "x": 5.34, + "y": -12.57, + "curve": [ 0.492, 5.34, 0.55, 5.24, 0.482, -7.36, 0.504, 4.03 ] + }, + { + "time": 0.5667, + "x": 5.11, + "y": 4.01, + "curve": [ 0.658, 4.45, 0.679, 3.19, 0.649, 3.98, 0.642, -16.84 ] + }, + { + "time": 0.7, + "x": 2.8, + "y": -16.74, + "curve": [ 0.787, 1.15, 0.881, -1.29, 0.772, -16.62, 0.82, 8.95 ] + }, + { + "time": 0.9, + "x": -1.72, + "y": 8.91, + "curve": [ 0.961, -3.06, 1.025, -3.58, 0.975, 8.87, 0.951, -1.37 ] + }, + { + "time": 1.1, + "x": -3.58, + "y": -1.45, + "curve": [ 1.292, -3.58, 2.002, -2.4, 1.292, -1.56, 1.975, -1.45 ] + }, + { + "time": 2.1667, + "x": -1.39, + "y": -1.45, + "curve": [ 2.25, -0.88, 2.503, 1.38, 2.283, -1.45, 2.603, -12.44 ] + }, + { + "time": 2.6667, + "x": 2.13, + "y": -14.45, + "curve": [ 2.766, 2.59, 2.999, 2.81, 2.835, -19.73, 3.003, -25.2 ] + }, + { + "time": 3.1333, + "x": 2.91, + "y": -26.08, + "curve": [ 3.392, 3.1, 4.199, 4.05, 3.483, -28.44, 4.129, -27.23 ] + }, + { + "time": 4.3667, + "x": 4.81, + "y": -19.59, + "curve": [ 4.429, 5.1, 4.594, 8.54, 4.538, -14.08, 4.583, -7.88 ] + }, + { + "time": 4.6667, + "x": 8.65, + "y": -4.56, + "curve": [ 4.794, 8.86, 4.806, 5.93, 4.691, -3.59, 4.8, -1.61 ] + }, + { "time": 4.9333, "x": 5.8, "y": -1.99 } + ] + } + }, + "ik": { + "front-foot-ik": [ + { "mix": 0 } + ], + "front-leg-ik": [ + { "mix": 0, "bendPositive": false } + ], + "rear-foot-ik": [ + { "mix": 0.005 } + ], + "rear-leg-ik": [ + { "mix": 0.005, "bendPositive": false } + ] + } + }, + "hoverboard": { + "slots": { + "exhaust1": { + "attachment": [ + { "name": "hoverglow-small" } + ] + }, + "exhaust2": { + "attachment": [ + { "name": "hoverglow-small" } + ] + }, + "exhaust3": { + "attachment": [ + { "name": "hoverglow-small" } + ] + }, + "front-fist": { + "attachment": [ + { "name": "front-fist-open" } + ] + }, + "hoverboard-board": { + "attachment": [ + { "name": "hoverboard-board" } + ] + }, + "hoverboard-thruster-front": { + "attachment": [ + { "name": "hoverboard-thruster" } + ] + }, + "hoverboard-thruster-rear": { + "attachment": [ + { "name": "hoverboard-thruster" } + ] + }, + "hoverglow-front": { + "attachment": [ + { "name": "hoverglow-small" } + ] + }, + "hoverglow-rear": { + "attachment": [ + { "name": "hoverglow-small" } + ] + }, + "side-glow1": { + "attachment": [ + { "name": "hoverglow-small" }, + { "time": 0.9667 } + ] + }, + "side-glow2": { + "attachment": [ + { "time": 0.0667, "name": "hoverglow-small" }, + { "time": 1 } + ] + }, + "side-glow3": { + "attachment": [ + { "name": "hoverglow-small" }, + { "time": 0.9667 } + ] + } + }, + "bones": { + "hoverboard-controller": { + "translate": [ + { + "x": 319.55, + "y": -1.59, + "curve": [ 0.064, 319.55, 0.2, 347.85, 0.058, -1.2, 0.2, 23.11 ] + }, + { + "time": 0.2667, + "x": 347.66, + "y": 39.62, + "curve": [ 0.35, 347.41, 0.476, 341.47, 0.323, 53.58, 0.44, 85.82 ] + }, + { + "time": 0.5333, + "x": 338.47, + "y": 85.72, + "curve": [ 0.603, 334.83, 0.913, 319.65, 0.621, 85.62, 0.88, -1.53 ] + }, + { "time": 1, "x": 319.55, "y": -1.59 } + ] + }, + "hip": { + "translate": [ + { + "x": -53.49, + "y": 32.14, + "curve": [ 0.061, -53.77, 0.093, -51.81, 0.044, 16.34, 0.063, 9.67 ] + }, + { + "time": 0.1333, + "x": -49.31, + "y": 7.01, + "curve": [ 0.3, -35.27, 0.461, -20.06, 0.314, 9.52, 0.408, 121.09 ] + }, + { + "time": 0.5667, + "x": -20.06, + "y": 122.72, + "curve": [ 0.716, -20.09, 0.912, -53.29, 0.753, 121.8, 0.946, 51.85 ] + }, + { "time": 1, "x": -53.49, "y": 32.14 } + ] + }, + "exhaust1": { + "scale": [ + { + "x": 1.593, + "y": 0.964, + "curve": [ 0.033, 1.593, 0.1, 1, 0.033, 0.964, 0.1, 0.713 ] + }, + { + "time": 0.1333, + "y": 0.713, + "curve": [ 0.15, 1, 0.183, 1.774, 0.15, 0.713, 0.183, 0.883 ] + }, + { + "time": 0.2, + "x": 1.774, + "y": 0.883, + "curve": [ 0.242, 1.774, 0.325, 1.181, 0.242, 0.883, 0.325, 0.649 ] + }, + { + "time": 0.3667, + "x": 1.181, + "y": 0.649, + "curve": [ 0.408, 1.181, 0.492, 1.893, 0.408, 0.649, 0.492, 0.819 ] + }, + { + "time": 0.5333, + "x": 1.893, + "y": 0.819, + "curve": [ 0.558, 1.893, 0.608, 1.18, 0.558, 0.819, 0.608, 0.686 ] + }, + { + "time": 0.6333, + "x": 1.18, + "y": 0.686, + "curve": [ 0.658, 1.18, 0.708, 1.903, 0.658, 0.686, 0.708, 0.855 ] + }, + { + "time": 0.7333, + "x": 1.903, + "y": 0.855, + "curve": [ 0.767, 1.903, 0.833, 1.311, 0.767, 0.855, 0.833, 0.622 ] + }, + { + "time": 0.8667, + "x": 1.311, + "y": 0.622, + "curve": [ 0.9, 1.311, 0.967, 1.593, 0.9, 0.622, 0.967, 0.964 ] + }, + { "time": 1, "x": 1.593, "y": 0.964 } + ] + }, + "exhaust2": { + "scale": [ + { + "x": 1.88, + "y": 0.832, + "curve": [ 0.025, 1.88, 0.075, 1.311, 0.025, 0.832, 0.075, 0.686 ] + }, + { + "time": 0.1, + "x": 1.311, + "y": 0.686, + "curve": [ 0.133, 1.311, 0.2, 2.01, 0.133, 0.686, 0.208, 0.736 ] + }, + { + "time": 0.2333, + "x": 2.01, + "y": 0.769, + "curve": [ 0.267, 2.01, 0.333, 1, 0.282, 0.831, 0.333, 0.91 ] + }, + { + "time": 0.3667, + "y": 0.91, + "curve": [ 0.4, 1, 0.467, 1.699, 0.4, 0.91, 0.474, 0.891 ] + }, + { + "time": 0.5, + "x": 1.699, + "y": 0.86, + "curve": [ 0.517, 1.699, 0.55, 1.181, 0.54, 0.813, 0.55, 0.713 ] + }, + { + "time": 0.5667, + "x": 1.181, + "y": 0.713, + "curve": [ 0.617, 1.181, 0.717, 1.881, 0.617, 0.713, 0.717, 0.796 ] + }, + { + "time": 0.7667, + "x": 1.881, + "y": 0.796, + "curve": [ 0.8, 1.881, 0.867, 1.3, 0.8, 0.796, 0.867, 0.649 ] + }, + { + "time": 0.9, + "x": 1.3, + "y": 0.649, + "curve": [ 0.925, 1.3, 0.975, 1.88, 0.925, 0.649, 0.975, 0.832 ] + }, + { "time": 1, "x": 1.88, "y": 0.832 } + ] + }, + "hoverboard-thruster-front": { + "rotate": [ + { + "curve": [ 0.125, 0, 0.375, 24.06 ] + }, + { + "time": 0.5, + "value": 24.06, + "curve": [ 0.625, 24.06, 0.875, 0 ] + }, + { "time": 1 } + ] + }, + "hoverglow-front": { + "scale": [ + { + "x": 0.849, + "y": 1.764, + "curve": [ 0.017, 0.849, 0.05, 0.835, 0.017, 1.764, 0.05, 2.033 ] + }, + { + "time": 0.0667, + "x": 0.835, + "y": 2.033, + "curve": [ 0.092, 0.835, 0.142, 0.752, 0.092, 2.033, 0.142, 1.584 ] + }, + { + "time": 0.1667, + "x": 0.752, + "y": 1.584, + "curve": [ 0.183, 0.752, 0.217, 0.809, 0.183, 1.584, 0.217, 1.71 ] + }, + { + "time": 0.2333, + "x": 0.809, + "y": 1.71, + "curve": [ 0.25, 0.809, 0.283, 0.717, 0.25, 1.71, 0.283, 1.45 ] + }, + { + "time": 0.3, + "x": 0.717, + "y": 1.45, + "curve": [ 0.317, 0.717, 0.35, 0.777, 0.317, 1.45, 0.35, 1.698 ] + }, + { + "time": 0.3667, + "x": 0.777, + "y": 1.698, + "curve": [ 0.4, 0.781, 0.45, 0.685, 0.375, 1.698, 0.45, 1.173 ] + }, + { + "time": 0.4667, + "x": 0.685, + "y": 1.173, + "curve": [ 0.492, 0.685, 0.542, 0.825, 0.492, 1.173, 0.542, 1.572 ] + }, + { + "time": 0.5667, + "x": 0.825, + "y": 1.572, + "curve": [ 0.611, 0.816, 0.63, 0.727, 0.611, 1.577, 0.606, 1.255 ] + }, + { + "time": 0.6667, + "x": 0.725, + "y": 1.241, + "curve": [ 0.692, 0.725, 0.742, 0.895, 0.692, 1.241, 0.749, 1.799 ] + }, + { + "time": 0.7667, + "x": 0.895, + "y": 1.857, + "curve": [ 0.783, 0.895, 0.796, 0.892, 0.796, 1.955, 0.817, 1.962 ] + }, + { + "time": 0.8333, + "x": 0.845, + "y": 1.962, + "curve": [ 0.845, 0.831, 0.883, 0.802, 0.85, 1.962, 0.872, 1.704 ] + }, + { + "time": 0.9, + "x": 0.802, + "y": 1.491, + "curve": [ 0.917, 0.802, 0.95, 0.845, 0.907, 1.441, 0.936, 1.508 ] + }, + { + "time": 0.9667, + "x": 0.845, + "y": 1.627, + "curve": [ 0.975, 0.845, 0.992, 0.849, 0.973, 1.652, 0.992, 1.764 ] + }, + { "time": 1, "x": 0.849, "y": 1.764 } + ] + }, + "hoverboard-thruster-rear": { + "rotate": [ + { + "curve": [ 0.125, 0, 0.375, 24.06 ] + }, + { + "time": 0.5, + "value": 24.06, + "curve": [ 0.625, 24.06, 0.875, 0 ] + }, + { "time": 1 } + ] + }, + "hoverglow-rear": { + "scale": [ + { + "x": 0.845, + "y": 1.31, + "curve": [ 0.017, 0.845, 0.117, 0.899, 0.017, 1.31, 0.117, 2.033 ] + }, + { + "time": 0.1333, + "x": 0.899, + "y": 2.033, + "curve": [ 0.15, 0.899, 0.183, 0.752, 0.15, 2.033, 0.183, 1.574 ] + }, + { + "time": 0.2, + "x": 0.752, + "y": 1.574, + "curve": [ 0.225, 0.752, 0.275, 0.809, 0.225, 1.574, 0.275, 1.71 ] + }, + { + "time": 0.3, + "x": 0.809, + "y": 1.71, + "curve": [ 0.317, 0.809, 0.35, 0.717, 0.317, 1.71, 0.35, 1.397 ] + }, + { + "time": 0.3667, + "x": 0.717, + "y": 1.397, + "curve": [ 0.383, 0.717, 0.417, 0.777, 0.383, 1.397, 0.417, 1.45 ] + }, + { + "time": 0.4333, + "x": 0.777, + "y": 1.45, + "curve": [ 0.45, 0.777, 0.496, 0.689, 0.45, 1.45, 0.481, 1.168 ] + }, + { + "time": 0.5333, + "x": 0.685, + "y": 1.173, + "curve": [ 0.565, 0.682, 0.617, 0.758, 0.575, 1.177, 0.617, 1.297 ] + }, + { + "time": 0.6333, + "x": 0.758, + "y": 1.297, + "curve": [ 0.658, 0.758, 0.708, 0.725, 0.658, 1.297, 0.708, 1.241 ] + }, + { + "time": 0.7333, + "x": 0.725, + "y": 1.241, + "curve": [ 0.772, 0.732, 0.796, 0.893, 0.782, 1.238, 0.778, 1.854 ] + }, + { + "time": 0.8333, + "x": 0.895, + "y": 1.857, + "curve": [ 0.878, 0.9, 0.992, 0.845, 0.88, 1.86, 0.992, 1.31 ] + }, + { "time": 1, "x": 0.845, "y": 1.31 } + ] + }, + "front-upper-arm": { + "rotate": [ + { + "value": -85.92, + "curve": [ 0.08, -85.59, 0.284, -62.7 ] + }, + { + "time": 0.3667, + "value": -55.14, + "curve": [ 0.438, -48.65, 0.551, -43.21 ] + }, + { + "time": 0.6333, + "value": -43.21, + "curve": [ 0.716, -43.22, 0.908, -85.92 ] + }, + { "time": 1, "value": -85.92 } + ], + "translate": [ + { + "x": -0.59, + "y": -2.94, + "curve": [ 0.1, -1.21, 0.275, -1.74, 0.092, -2.94, 0.275, -6.39 ] + }, + { + "time": 0.3667, + "x": -1.74, + "y": -6.39, + "curve": [ 0.433, -1.74, 0.567, 0.72, 0.433, -6.39, 0.587, -4.48 ] + }, + { + "time": 0.6333, + "x": 0.72, + "y": -4.21, + "curve": [ 0.725, 0.72, 0.908, -0.08, 0.743, -3.57, 0.908, -2.94 ] + }, + { "time": 1, "x": -0.59, "y": -2.94 } + ] + }, + "front-fist": { + "rotate": [ + { + "value": 7.61, + "curve": [ 0.143, 7.62, 0.247, -23.17 ] + }, + { + "time": 0.2667, + "value": -26.56, + "curve": [ 0.281, -29.08, 0.351, -37.36 ] + }, + { + "time": 0.4333, + "value": -37.2, + "curve": [ 0.513, -37.05, 0.562, -29.88 ] + }, + { + "time": 0.6, + "value": -25.18, + "curve": [ 0.621, -22.58, 0.694, -3.98 ] + }, + { + "time": 0.8, + "value": 3.63, + "curve": [ 0.861, 8.03, 0.946, 7.57 ] + }, + { "time": 1, "value": 7.61 } + ], + "translate": [ + { + "curve": [ 0.117, 0, 0.35, 0.52, 0.117, 0, 0.35, -3.27 ] + }, + { + "time": 0.4667, + "x": 0.52, + "y": -3.27, + "curve": [ 0.6, 0.52, 0.867, 0, 0.6, -3.27, 0.867, 0 ] + }, + { "time": 1 } + ], + "shear": [ + { + "y": 19.83, + "curve": [ 0.117, 0, 0.35, 15.28, 0.117, 19.83, 0.35, 28.31 ] + }, + { + "time": 0.4667, + "x": 15.28, + "y": 28.31, + "curve": [ 0.6, 15.28, 0.867, 0, 0.6, 28.31, 0.867, 19.83 ] + }, + { "time": 1, "y": 19.83 } + ] + }, + "board-ik": { + "translate": [ + { + "x": 393.62, + "curve": [ 0.083, 393.62, 0.25, 393.48, 0.083, 0, 0.25, 117.69 ] + }, + { + "time": 0.3333, + "x": 393.48, + "y": 117.69, + "curve": [ 0.375, 393.48, 0.458, 393.62, 0.375, 117.69, 0.458, 83.82 ] + }, + { "time": 0.5, "x": 393.62, "y": 83.82 }, + { "time": 0.6667, "x": 393.62, "y": 30.15 }, + { "time": 1, "x": 393.62 } + ] + }, + "front-thigh": { + "translate": [ + { "x": -7.49, "y": 8.51 } + ] + }, + "front-leg-target": { + "translate": [ + { + "time": 0.3667, + "curve": [ 0.428, 10.83, 0.567, 12.78, 0.414, 7.29, 0.567, 8.79 ] + }, + { + "time": 0.6, + "x": 12.78, + "y": 8.79, + "curve": [ 0.692, 12.78, 0.772, 11.27, 0.692, 8.79, 0.766, 8.62 ] + }, + { "time": 0.8667 } + ] + }, + "rear-leg-target": { + "translate": [ + { + "time": 0.4667, + "curve": [ 0.492, 0, 0.534, 4.47, 0.492, 0, 0.542, 1.63 ] + }, + { + "time": 0.5667, + "x": 4.53, + "y": 1.77, + "curve": [ 0.622, 4.64, 0.717, 3.31, 0.615, 2.06, 0.71, 2.1 ] + }, + { "time": 0.8 } + ] + }, + "exhaust3": { + "scale": [ + { + "x": 1.882, + "y": 0.81, + "curve": [ 0.017, 1.882, 0.167, 1.3, 0.017, 0.81, 0.167, 0.649 ] + }, + { + "time": 0.2, + "x": 1.3, + "y": 0.649, + "curve": [ 0.225, 1.3, 0.275, 2.051, 0.225, 0.649, 0.275, 0.984 ] + }, + { + "time": 0.3, + "x": 2.051, + "y": 0.984, + "curve": [ 0.325, 2.051, 0.375, 1.311, 0.325, 0.984, 0.384, 0.715 ] + }, + { + "time": 0.4, + "x": 1.311, + "y": 0.686, + "curve": [ 0.433, 1.311, 0.5, 1.86, 0.426, 0.638, 0.5, 0.537 ] + }, + { + "time": 0.5333, + "x": 1.86, + "y": 0.537, + "curve": [ 0.567, 1.86, 0.633, 1.187, 0.567, 0.537, 0.604, 0.854 ] + }, + { + "time": 0.6667, + "x": 1.187, + "y": 0.854, + "curve": [ 0.7, 1.187, 0.767, 1.549, 0.707, 0.854, 0.774, 0.775 ] + }, + { + "time": 0.8, + "x": 1.549, + "y": 0.746, + "curve": [ 0.817, 1.549, 0.85, 1.181, 0.815, 0.729, 0.85, 0.713 ] + }, + { + "time": 0.8667, + "x": 1.181, + "y": 0.713, + "curve": [ 0.9, 1.181, 0.967, 1.882, 0.9, 0.713, 0.967, 0.81 ] + }, + { "time": 1, "x": 1.882, "y": 0.81 } + ] + }, + "side-glow1": { + "rotate": [ + { "value": 51.12, "curve": "stepped" }, + { "time": 0.0667, "value": 43.82, "curve": "stepped" }, + { "time": 0.1, "value": 40.95, "curve": "stepped" }, + { "time": 0.1667, "value": 27.78, "curve": "stepped" }, + { "time": 0.2, "value": 10.24, "curve": "stepped" }, + { "time": 0.2667, "curve": "stepped" }, + { "time": 0.8, "value": -25.81 } + ], + "translate": [ + { "x": 338.28, "y": 40.22, "curve": "stepped" }, + { "time": 0.0667, "x": 331.2, "y": 30.39, "curve": "stepped" }, + { "time": 0.1, "x": 318.63, "y": 20.59, "curve": "stepped" }, + { "time": 0.1667, "x": 302.45, "y": 9.64, "curve": "stepped" }, + { "time": 0.2, "x": 276.87, "y": 1.13, "curve": "stepped" }, + { "time": 0.2667, "x": 248.16, "curve": "stepped" }, + { "time": 0.3, "x": 221.36, "curve": "stepped" }, + { "time": 0.3667, "x": 195.69, "curve": "stepped" }, + { "time": 0.4, "x": 171.08, "curve": "stepped" }, + { "time": 0.4667, "x": 144.84, "curve": "stepped" }, + { "time": 0.5, "x": 121.22, "curve": "stepped" }, + { "time": 0.5667, "x": 91.98, "curve": "stepped" }, + { "time": 0.6, "x": 62.63, "curve": "stepped" }, + { "time": 0.6667, "x": 30.78, "curve": "stepped" }, + { "time": 0.7, "curve": "stepped" }, + { "time": 0.7667, "x": -28.45, "curve": "stepped" }, + { "time": 0.8, "x": -67.49, "y": 16.82, "curve": "stepped" }, + { "time": 0.8667, "x": -83.07, "y": 24.36, "curve": "stepped" }, + { "time": 0.9, "x": -93.81, "y": 29.55 } + ], + "scale": [ + { "x": 0.535, "curve": "stepped" }, + { "time": 0.0667, "x": 0.594, "curve": "stepped" }, + { "time": 0.1, "x": 0.844, "curve": "stepped" }, + { "time": 0.1667, "curve": "stepped" }, + { "time": 0.8, "x": 0.534, "curve": "stepped" }, + { "time": 0.8667, "x": 0.428, "y": 0.801, "curve": "stepped" }, + { "time": 0.9, "x": 0.349, "y": 0.654 } + ] + }, + "side-glow2": { + "rotate": [ + { "time": 0.0667, "value": 51.12, "curve": "stepped" }, + { "time": 0.1, "value": 43.82, "curve": "stepped" }, + { "time": 0.1667, "value": 40.95, "curve": "stepped" }, + { "time": 0.2, "value": 27.78, "curve": "stepped" }, + { "time": 0.2667, "value": 10.24, "curve": "stepped" }, + { "time": 0.3, "curve": "stepped" }, + { "time": 0.8667, "value": -25.81 } + ], + "translate": [ + { "time": 0.0667, "x": 338.28, "y": 40.22, "curve": "stepped" }, + { "time": 0.1, "x": 331.2, "y": 30.39, "curve": "stepped" }, + { "time": 0.1667, "x": 318.63, "y": 20.59, "curve": "stepped" }, + { "time": 0.2, "x": 302.45, "y": 9.64, "curve": "stepped" }, + { "time": 0.2667, "x": 276.87, "y": 1.13, "curve": "stepped" }, + { "time": 0.3, "x": 248.16, "curve": "stepped" }, + { "time": 0.3667, "x": 221.36, "curve": "stepped" }, + { "time": 0.4, "x": 195.69, "curve": "stepped" }, + { "time": 0.4667, "x": 171.08, "curve": "stepped" }, + { "time": 0.5, "x": 144.84, "curve": "stepped" }, + { "time": 0.5667, "x": 121.22, "curve": "stepped" }, + { "time": 0.6, "x": 91.98, "curve": "stepped" }, + { "time": 0.6667, "x": 62.63, "curve": "stepped" }, + { "time": 0.7, "x": 30.78, "curve": "stepped" }, + { "time": 0.7667, "curve": "stepped" }, + { "time": 0.8, "x": -28.45, "curve": "stepped" }, + { "time": 0.8667, "x": -67.49, "y": 16.82, "curve": "stepped" }, + { "time": 0.9, "x": -83.07, "y": 24.36, "curve": "stepped" }, + { "time": 0.9667, "x": -93.81, "y": 29.55 } + ], + "scale": [ + { "time": 0.0667, "x": 0.535, "curve": "stepped" }, + { "time": 0.1, "x": 0.594, "curve": "stepped" }, + { "time": 0.1667, "x": 0.844, "curve": "stepped" }, + { "time": 0.2, "curve": "stepped" }, + { "time": 0.8667, "x": 0.534, "curve": "stepped" }, + { "time": 0.9, "x": 0.428, "y": 0.801, "curve": "stepped" }, + { "time": 0.9667, "x": 0.349, "y": 0.654 } + ] + }, + "torso": { + "rotate": [ + { + "value": -34.73, + "curve": [ 0.034, -36.31, 0.162, -39.33 ] + }, + { + "time": 0.2667, + "value": -39.37, + "curve": [ 0.384, -39.37, 0.491, -29.52 ] + }, + { + "time": 0.5, + "value": -28.86, + "curve": [ 0.525, -26.95, 0.571, -21.01 ] + }, + { + "time": 0.6333, + "value": -21.01, + "curve": [ 0.725, -21.01, 0.969, -33.35 ] + }, + { "time": 1, "value": -34.73 } + ] + }, + "neck": { + "rotate": [ + { + "value": 10.2, + "curve": [ 0.07, 12.09, 0.189, 16.03 ] + }, + { + "time": 0.2667, + "value": 16.14, + "curve": [ 0.333, 16.14, 0.449, 8.03 ] + }, + { + "time": 0.5, + "value": 5.83, + "curve": [ 0.542, 4.02, 0.6, 2.68 ] + }, + { + "time": 0.6333, + "value": 2.68, + "curve": [ 0.725, 2.68, 0.943, 8.57 ] + }, + { "time": 1, "value": 10.2 } + ] + }, + "head": { + "rotate": [ + { + "value": 10.2, + "curve": [ 0.044, 11.52, 0.2, 16.12 ] + }, + { + "time": 0.2667, + "value": 16.14, + "curve": [ 0.375, 16.17, 0.492, 2.65 ] + }, + { + "time": 0.6333, + "value": 2.68, + "curve": [ 0.725, 2.7, 0.963, 9.26 ] + }, + { "time": 1, "value": 10.2 } + ], + "translate": [ + { + "curve": [ 0.03, -0.24, 0.2, -4.22, 0.051, -1.06, 0.2, -3.62 ] + }, + { + "time": 0.2667, + "x": -4.22, + "y": -3.62, + "curve": [ 0.358, -4.22, 0.542, 0.84, 0.358, -3.62, 0.542, 6.01 ] + }, + { + "time": 0.6333, + "x": 0.84, + "y": 6.01, + "curve": [ 0.725, 0.84, 0.939, 0.32, 0.725, 6.01, 0.945, 1.14 ] + }, + { "time": 1 } + ] + }, + "front-bracer": { + "rotate": [ + { + "value": -11.18, + "curve": [ 0.064, -14.82, 0.25, -20.01 ] + }, + { + "time": 0.3333, + "value": -20.01, + "curve": [ 0.429, -20.12, 0.58, 5.12 ] + }, + { + "time": 0.6, + "value": 8.67, + "curve": [ 0.617, 11.72, 0.687, 20.52 ] + }, + { + "time": 0.7667, + "value": 20.55, + "curve": [ 0.848, 20.7, 0.963, -9.43 ] + }, + { "time": 1, "value": -11.18 } + ] + }, + "hair3": { + "rotate": [ + { + "value": 9.61, + "curve": [ 0.014, 8.51, 0.075, 2.63 ] + }, + { + "time": 0.1, + "value": 2.63, + "curve": [ 0.15, 2.63, 0.25, 13.52 ] + }, + { + "time": 0.3, + "value": 13.52, + "curve": [ 0.35, 13.52, 0.45, 11.28 ] + }, + { + "time": 0.5, + "value": 11.28, + "curve": [ 0.575, 11.28, 0.725, 18.13 ] + }, + { + "time": 0.8, + "value": 18.13, + "curve": [ 0.85, 18.13, 0.978, 11.07 ] + }, + { "time": 1, "value": 9.61 } + ] + }, + "hair4": { + "rotate": [ + { + "value": -17.7, + "curve": [ 0.008, -17.7, 0.025, -23.73 ] + }, + { + "time": 0.0333, + "value": -23.73, + "curve": [ 0.067, -23.73, 0.154, -4.4 ] + }, + { + "time": 0.1667, + "value": -1.92, + "curve": [ 0.197, 4.09, 0.236, 12.91 ] + }, + { + "time": 0.2667, + "value": 17.56, + "curve": [ 0.301, 22.68, 0.342, 27.97 ] + }, + { + "time": 0.3667, + "value": 27.97, + "curve": [ 0.4, 27.97, 0.467, -1.45 ] + }, + { + "time": 0.5, + "value": -1.45, + "curve": [ 0.517, -1.45, 0.55, 3.16 ] + }, + { + "time": 0.5667, + "value": 3.16, + "curve": [ 0.583, 3.16, 0.617, -8.9 ] + }, + { + "time": 0.6333, + "value": -8.9, + "curve": [ 0.642, -8.9, 0.658, -5.4 ] + }, + { + "time": 0.6667, + "value": -5.4, + "curve": [ 0.683, -5.4, 0.717, -15.32 ] + }, + { + "time": 0.7333, + "value": -15.32, + "curve": [ 0.75, -15.32, 0.783, -9.19 ] + }, + { + "time": 0.8, + "value": -9.19, + "curve": [ 0.817, -9.19, 0.85, -23.6 ] + }, + { + "time": 0.8667, + "value": -23.6, + "curve": [ 0.883, -23.6, 0.917, -17.38 ] + }, + { + "time": 0.9333, + "value": -17.38, + "curve": [ 0.942, -17.38, 0.958, -20.46 ] + }, + { + "time": 0.9667, + "value": -20.46, + "curve": [ 0.975, -20.46, 0.992, -17.7 ] + }, + { "time": 1, "value": -17.7 } + ] + }, + "hair1": { + "rotate": [ + { + "value": 9.61, + "curve": [ 0.06, 9.04, 0.25, 8.9 ] + }, + { + "time": 0.3333, + "value": 8.9, + "curve": [ 0.392, 8.9, 0.508, 14.58 ] + }, + { + "time": 0.5667, + "value": 14.58, + "curve": [ 0.675, 14.58, 0.956, 10.28 ] + }, + { "time": 1, "value": 9.61 } + ] + }, + "hair2": { + "rotate": [ + { + "value": -3.82, + "curve": [ 0.017, -3.82, 0.064, -9.16 ] + }, + { + "time": 0.1333, + "value": -9.09, + "curve": [ 0.178, -9.04, 0.234, 1.29 ] + }, + { + "time": 0.2667, + "value": 5.98, + "curve": [ 0.276, 7.27, 0.336, 17.1 ] + }, + { + "time": 0.3667, + "value": 17.1, + "curve": [ 0.413, 17.1, 0.467, 1.59 ] + }, + { + "time": 0.5, + "value": 1.59, + "curve": [ 0.533, 1.59, 0.567, 13.63 ] + }, + { + "time": 0.6, + "value": 13.63, + "curve": [ 0.617, 13.63, 0.683, 0.78 ] + }, + { + "time": 0.7, + "value": 0.78, + "curve": [ 0.717, 0.78, 0.75, 12.01 ] + }, + { + "time": 0.7667, + "value": 11.9, + "curve": [ 0.792, 11.73, 0.817, -0.85 ] + }, + { + "time": 0.8333, + "value": -0.85, + "curve": [ 0.85, -0.85, 0.88, 1.99 ] + }, + { + "time": 0.9, + "value": 1.82, + "curve": [ 0.916, 1.68, 0.95, -6.9 ] + }, + { + "time": 0.9667, + "value": -6.9, + "curve": [ 0.975, -6.9, 0.992, -3.82 ] + }, + { "time": 1, "value": -3.82 } + ] + }, + "rear-upper-arm": { + "rotate": [ + { + "value": 31.65, + "curve": [ 0.108, 31.65, 0.325, 13.01 ] + }, + { + "time": 0.4333, + "value": 13.01, + "curve": [ 0.71, 13.01, 0.917, 31.65 ] + }, + { "time": 1, "value": 31.65 } + ] + }, + "rear-bracer": { + "rotate": [ + { + "value": 31, + "curve": [ 0.108, 31, 0.325, 12.76 ] + }, + { + "time": 0.4333, + "value": 12.79, + "curve": [ 0.587, 12.82, 0.917, 31 ] + }, + { "time": 1, "value": 31 } + ] + }, + "gun": { + "rotate": [ + { + "value": 1.95, + "curve": [ 0.083, 1.95, 0.245, 36.73 ] + }, + { + "time": 0.3333, + "value": 36.71, + "curve": [ 0.439, 36.69, 0.589, 10.68 ] + }, + { + "time": 0.6333, + "value": 8.75, + "curve": [ 0.701, 5.81, 0.917, 1.95 ] + }, + { "time": 1, "value": 1.95 } + ] + }, + "torso2": { + "rotate": [ + { + "curve": [ 0.033, 0, 0.1, 2.35 ] + }, + { + "time": 0.1333, + "value": 2.35, + "curve": [ 0.225, 2.35, 0.408, -2.4 ] + }, + { + "time": 0.5, + "value": -2.4, + "curve": [ 0.567, -2.4, 0.7, 1.44 ] + }, + { + "time": 0.7667, + "value": 1.44, + "curve": [ 0.825, 1.44, 0.942, 0 ] + }, + { "time": 1 } + ] + }, + "torso3": { + "rotate": [ + { + "curve": [ 0.063, 0.77, 0.106, 1.42 ] + }, + { + "time": 0.1667, + "value": 1.42, + "curve": [ 0.259, 1.42, 0.344, -1.25 ] + }, + { + "time": 0.4667, + "value": -1.26, + "curve": [ 0.656, -1.26, 0.917, -0.78 ] + }, + { "time": 1 } + ] + }, + "head-control": { + "translate": [ + { + "x": 0.37, + "y": -11.17, + "curve": [ 0.133, 0.37, 0.335, -10.23, 0.133, -11.17, 0.335, 3.15 ] + }, + { + "time": 0.5333, + "x": -10.23, + "y": 3.15, + "curve": [ 0.71, -10.23, 0.883, 0.37, 0.71, 3.15, 0.883, -11.17 ] + }, + { "time": 1, "x": 0.37, "y": -11.17 } + ] + }, + "front-shoulder": { + "translate": [ + { + "x": 1.46, + "y": 10.15, + "curve": [ 0.103, 1.46, 0.249, 1.36, 0.103, 10.15, 0.249, -4.39 ] + }, + { + "time": 0.4, + "x": 1.36, + "y": -4.39, + "curve": [ 0.621, 1.36, 0.85, 1.46, 0.621, -4.39, 0.85, 10.15 ] + }, + { "time": 1, "x": 1.46, "y": 10.15 } + ] + }, + "back-shoulder": { + "translate": [ + { + "x": 1.4, + "y": 0.44, + "curve": [ 0.088, 1.4, 0.208, -2.47, 0.088, 0.44, 0.208, 8.61 ] + }, + { + "time": 0.3333, + "x": -2.47, + "y": 8.61, + "curve": [ 0.572, -2.47, 0.833, 1.4, 0.572, 8.61, 0.833, 0.44 ] + }, + { "time": 1, "x": 1.4, "y": 0.44 } + ] + } + }, + "transform": { + "front-foot-board-transform": [ + { "mixRotate": 0.997 } + ], + "rear-foot-board-transform": [ + {} + ], + "toes-board": [ + { "mixX": 0, "mixScaleX": 0, "mixShearY": 0 } + ] + }, + "attachments": { + "default": { + "front-foot": { + "front-foot": { + "deform": [ + { + "offset": 26, + "vertices": [ -0.02832, -5.37024, -0.02832, -5.37024, 3.8188, -3.7757, -0.02832, -5.37024, -3.82159, 3.77847 ] + } + ] + } + }, + "front-shin": { + "front-shin": { + "deform": [ + { + "offset": 14, + "vertices": [ 0.5298, -1.12677, -0.85507, -4.20587, -11.35158, -10.19225, -10.79865, -8.43765, -6.06447, -6.89757, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.54892, -3.06021, 1.48463, -2.29663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4.80437, -7.01817 ] + }, + { + "time": 0.3667, + "offset": 14, + "vertices": [ 0.5298, -1.12677, -11.66571, -9.07211, -25.65866, -17.53735, -25.53217, -16.50978, -11.78232, -11.26097, 0, 0, 0.60487, -1.63589, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.60487, -1.63589, 0, 0, -2.64522, -7.35739, 1.48463, -2.29663, 0, 0, 0, 0, 0, 0, 0.60487, -1.63589, 0.60487, -1.63589, 0.60487, -1.63589, 0.60487, -1.63589, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.60487, -1.63589, 0, 0, -10.06873, -12.0999 ] + }, + { + "time": 0.5333, + "offset": 14, + "vertices": [ 0.5298, -1.12677, -0.85507, -4.20587, -7.00775, -8.24771, -6.45482, -6.49312, -6.06447, -6.89757, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.54892, -3.06021, 1.48463, -2.29663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4.80437, -7.01817 ] + }, + { + "time": 1, + "offset": 14, + "vertices": [ 0.5298, -1.12677, -0.85507, -4.20587, -11.35158, -10.19225, -10.79865, -8.43765, -6.06447, -6.89757, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.54892, -3.06021, 1.48463, -2.29663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4.80437, -7.01817 ] + } + ] + } + }, + "hoverboard-board": { + "hoverboard-board": { + "deform": [ + { + "curve": [ 0.067, 0, 0.2, 1 ] + }, + { + "time": 0.2667, + "offset": 1, + "vertices": [ 2.45856, 0, 0, 0, 0, 0, 0, 0, 0, 3.55673, -3.0E-4, 3.55673, -3.0E-4, 0, 0, 0, 0, 0, 0, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, -7.6E-4, -9.84158, 0, 0, 0, 0, 0, 0, 0, 0, -4.90558, 0.11214, -9.40706, 6.2E-4, -6.34871, 4.3E-4, -6.34925, -6.57018, -6.34925, -6.57018, -6.34871, 4.3E-4, -2.3308, 1.7E-4, -2.33133, -6.57045, -2.33133, -6.57045, -2.3308, 1.7E-4, 0, 0, 1.2E-4, 2.45856, 1.2E-4, 2.45856, 1.2E-4, 2.45856, 1.2E-4, 2.45856, 3.3297, 4.44005, 3.3297, 4.44005, 3.3297, 4.44005, 1.2E-4, 2.45856, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2.46227, 1.7E-4, -2.46227, 1.7E-4, -2.52316, 1.1313, -2.52316, 1.1313, -2.52316, 1.1313, 1.2E-4, 2.45856, 1.2E-4, 2.45856, -9.40694, 2.45918, 1.88063, 0.44197, -2.9E-4, -3.54808, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2.52316, 1.1313, -2.52316, 1.1313, -2.52316, 1.1313, -2.46227, 1.7E-4, -2.46227, 1.7E-4, -2.46227, 1.7E-4, 0, 0, 0, 0, 1.2E-4, 2.45856 ], + "curve": [ 0.45, 0, 0.817, 1 ] + }, + { "time": 1 } + ] + } + }, + "rear-foot": { + "rear-foot": { + "deform": [ + { + "offset": 28, + "vertices": [ -1.93078, 1.34782, -0.31417, 2.33363, 3.05122, 0.33946, 2.31472, -2.01678, 2.17583, -2.05795, -0.04277, -2.99459, 1.15429, 0.26328, 0.97501, -0.67169 ] + } + ] + } + } + } + } + }, + "idle": { + "slots": { + "front-fist": { + "attachment": [ + { "name": "front-fist-open" } + ] + } + }, + "bones": { + "front-foot-target": { + "translate": [ + { "x": -69.06 } + ] + }, + "hip": { + "rotate": [ + { + "curve": [ 0.073, 0.35, 0.303, 1.27 ] + }, + { + "time": 0.4, + "value": 1.28, + "curve": [ 0.615, 1.3, 0.847, -1.41 ] + }, + { + "time": 1.2, + "value": -1.38, + "curve": [ 1.344, -1.37, 1.602, -0.28 ] + }, + { "time": 1.6667 } + ], + "translate": [ + { + "x": -11.97, + "y": -23.15, + "curve": [ 0.059, -12.96, 0.258, -15.19, 0.142, -23.15, 0.341, -24.89 ] + }, + { + "time": 0.4667, + "x": -15.14, + "y": -26.74, + "curve": [ 0.62, -15.1, 0.788, -13.28, 0.597, -28.66, 0.75, -30.01 ] + }, + { + "time": 0.9, + "x": -12.02, + "y": -30.01, + "curve": [ 0.978, -11.13, 1.175, -9.05, 1.036, -29.94, 1.234, -28.08 ] + }, + { + "time": 1.3333, + "x": -9.06, + "y": -26.64, + "curve": [ 1.501, -9.06, 1.614, -10.95, 1.454, -24.89, 1.609, -23.15 ] + }, + { "time": 1.6667, "x": -11.97, "y": -23.15 } + ] + }, + "rear-foot-target": { + "translate": [ + { "x": 48.87 } + ] + }, + "front-upper-arm": { + "rotate": [ + { + "value": -60.87, + "curve": [ 0.154, -60.85, 0.452, -68.65 ] + }, + { + "time": 0.8333, + "value": -68.65, + "curve": [ 1.221, -68.65, 1.542, -60.87 ] + }, + { "time": 1.6667, "value": -60.87 } + ] + }, + "front-bracer": { + "rotate": [ + { + "value": 42.46, + "curve": [ 0.029, 42.97, 0.134, 45.28 ] + }, + { + "time": 0.3333, + "value": 45.27, + "curve": [ 0.578, 45.26, 0.798, 40.07 ] + }, + { + "time": 0.8333, + "value": 39.74, + "curve": [ 0.878, 39.32, 1.019, 38.23 ] + }, + { + "time": 1.2, + "value": 38.22, + "curve": [ 1.377, 38.22, 1.619, 41.68 ] + }, + { "time": 1.6667, "value": 42.46 } + ] + }, + "rear-upper-arm": { + "rotate": [ + { + "value": 39.2, + "curve": [ 0.185, 39.22, 0.5, 29.37 ] + }, + { + "time": 0.6667, + "value": 29.37, + "curve": [ 0.917, 29.37, 1.417, 39.2 ] + }, + { "time": 1.6667, "value": 39.2 } + ] + }, + "head": { + "rotate": [ + { + "value": -6.75, + "curve": [ 0.176, -7.88, 0.349, -8.95 ] + }, + { + "time": 0.4667, + "value": -8.95, + "curve": [ 0.55, -8.95, 0.697, -6.77 ] + }, + { + "time": 0.8333, + "value": -5.44, + "curve": [ 0.88, -4.98, 1.05, -4.12 ] + }, + { + "time": 1.1333, + "value": -4.12, + "curve": [ 1.266, -4.12, 1.469, -5.48 ] + }, + { "time": 1.6667, "value": -6.75 } + ] + }, + "front-fist": { + "rotate": [ + { + "curve": [ 0.086, 0, 0.233, 2.48 ] + }, + { + "time": 0.3333, + "value": 4.13, + "curve": [ 0.429, 5.7, 0.711, 10.06 ] + }, + { + "time": 0.8333, + "value": 10.06, + "curve": [ 0.926, 10.06, 1.092, 4.21 ] + }, + { + "time": 1.2, + "value": 2.78, + "curve": [ 1.349, 0.8, 1.551, 0 ] + }, + { "time": 1.6667 } + ] + }, + "rear-bracer": { + "rotate": [ + { + "curve": [ 0.063, 0.54, 0.367, 3.39 ] + }, + { + "time": 0.5333, + "value": 3.39, + "curve": [ 0.696, 3.39, 0.939, -1.63 ] + }, + { + "time": 1.2, + "value": -1.61, + "curve": [ 1.42, -1.59, 1.574, -0.67 ] + }, + { "time": 1.6667 } + ] + }, + "gun": { + "rotate": [ + { + "curve": [ 0.099, 0.27, 0.367, 1.23 ] + }, + { + "time": 0.5333, + "value": 1.23, + "curve": [ 0.665, 1.23, 0.937, -0.56 ] + }, + { + "time": 1.1333, + "value": -0.55, + "curve": [ 1.316, -0.55, 1.582, -0.21 ] + }, + { "time": 1.6667 } + ] + }, + "torso": { + "rotate": [ + { + "value": -22.88, + "curve": [ 0.099, -23.45, 0.363, -24.74 ] + }, + { + "time": 0.5333, + "value": -24.74, + "curve": [ 0.706, -24.74, 0.961, -20.97 ] + }, + { + "time": 1.1333, + "value": -20.97, + "curve": [ 1.355, -20.97, 1.567, -22.28 ] + }, + { "time": 1.6667, "value": -22.88 } + ] + }, + "neck": { + "rotate": [ + { + "value": 3.78, + "curve": [ 0.167, 3.78, 0.5, 5.45 ] + }, + { + "time": 0.6667, + "value": 5.45, + "curve": [ 0.917, 5.45, 1.417, 3.78 ] + }, + { "time": 1.6667, "value": 3.78 } + ] + }, + "torso2": { + "rotate": [ + { + "curve": [ 0.067, 0.33, 0.341, 2.54 ] + }, + { + "time": 0.5333, + "value": 2.54, + "curve": [ 0.734, 2.55, 0.982, -0.94 ] + }, + { + "time": 1.1333, + "value": -0.93, + "curve": [ 1.365, -0.91, 1.549, -0.56 ] + }, + { "time": 1.6667 } + ] + }, + "torso3": { + "rotate": [ + { + "value": -2.15, + "curve": [ 0.052, -1.9, 0.384, -0.15 ] + }, + { + "time": 0.5333, + "value": -0.14, + "curve": [ 0.762, -0.13, 0.895, -3.1 ] + }, + { + "time": 1.1333, + "value": -3.1, + "curve": [ 1.348, -3.1, 1.592, -2.46 ] + }, + { "time": 1.6667, "value": -2.15 } + ] + }, + "hair1": { + "rotate": [ + { + "curve": [ 0.067, 0, 0.213, 2.86 ] + }, + { + "time": 0.2667, + "value": 3.65, + "curve": [ 0.358, 4.99, 0.535, 7.92 ] + }, + { + "time": 0.6667, + "value": 7.92, + "curve": [ 0.809, 7.92, 1.067, 5.49 ] + }, + { + "time": 1.1333, + "value": 4.7, + "curve": [ 1.245, 3.34, 1.525, 0 ] + }, + { "time": 1.6667 } + ] + }, + "hair2": { + "rotate": [ + { + "curve": [ 0.067, 0, 0.225, -7.97 ] + }, + { + "time": 0.2667, + "value": -9.75, + "curve": [ 0.316, -11.84, 0.519, -16.66 ] + }, + { + "time": 0.6667, + "value": -16.66, + "curve": [ 0.817, -16.66, 1.029, -11.43 ] + }, + { + "time": 1.1333, + "value": -9.14, + "curve": [ 1.25, -6.56, 1.525, 0 ] + }, + { "time": 1.6667 } + ] + }, + "hair3": { + "rotate": [ + { + "curve": [ 0.1, 0, 0.3, 1.32 ] + }, + { + "time": 0.4, + "value": 1.32, + "curve": [ 0.55, 1.32, 0.866, 0.93 ] + }, + { + "time": 1, + "value": 0.73, + "curve": [ 1.189, 0.46, 1.5, 0 ] + }, + { "time": 1.6667 } + ] + }, + "hair4": { + "rotate": [ + { + "curve": [ 0.118, -0.44, 0.3, -8.52 ] + }, + { + "time": 0.4, + "value": -8.52, + "curve": [ 0.55, -8.52, 0.85, 1.96 ] + }, + { + "time": 1, + "value": 1.96, + "curve": [ 1.167, 1.96, 1.577, 0.38 ] + }, + { "time": 1.6667 } + ] + }, + "head-control": { + "translate": [ + { + "curve": [ 0.098, 1.46, 0.3, 4.49, 0.17, 0.13, 0.316, -3.28 ] + }, + { + "time": 0.4, + "x": 4.55, + "y": -5.95, + "curve": [ 0.53, 4.64, 0.776, 2.59, 0.492, -8.89, 0.668, -14.21 ] + }, + { + "time": 0.8667, + "x": 1.42, + "y": -14.26, + "curve": [ 0.966, 0.15, 1.109, -2.91, 0.994, -14.26, 1.144, -10.58 ] + }, + { + "time": 1.2333, + "x": -3.02, + "y": -8.26, + "curve": [ 1.342, -3.02, 1.568, -1.48, 1.317, -6.1, 1.558, 0 ] + }, + { "time": 1.6667 } + ] + }, + "front-shoulder": { + "translate": [ + { + "curve": [ 0.21, 0, 0.525, -1.72, 0.21, 0, 0.525, 4.08 ] + }, + { + "time": 0.8333, + "x": -1.72, + "y": 4.08, + "curve": [ 1.15, -1.72, 1.46, 0, 1.15, 4.08, 1.46, 0 ] + }, + { "time": 1.6667 } + ] + } + } + }, + "idle-turn": { + "slots": { + "front-fist": { + "attachment": [ + { "name": "front-fist-open" } + ] + } + }, + "bones": { + "front-upper-arm": { + "rotate": [ + { + "value": -302.77, + "curve": [ 0, -406.9, 0.125, -420.87 ] + }, + { "time": 0.2667, "value": -420.87 } + ], + "translate": [ + { + "x": 2.24, + "y": -4.98, + "curve": [ 0.067, 2.24, 0.111, 0, 0.067, -4.98, 0.111, 0 ] + }, + { "time": 0.2667 } + ] + }, + "rear-upper-arm": { + "rotate": [ + { + "value": 248.56, + "curve": [ 0, 371.28, 0.062, 399.2 ] + }, + { "time": 0.1333, "value": 399.2 } + ], + "translate": [ + { + "x": -2.84, + "y": 37.28, + "curve": [ 0.033, -2.84, 0.069, 0, 0.033, 37.28, 0.069, 0 ] + }, + { "time": 0.1333 } + ] + }, + "gun": { + "rotate": [ + { + "value": -3.95, + "curve": [ 0, -10.4, 0.019, -20.43 ] + }, + { + "time": 0.0333, + "value": -20.45, + "curve": [ 0.044, -20.47, 0.125, 0 ] + }, + { "time": 0.2 } + ] + }, + "neck": { + "rotate": [ + { + "value": 17.2, + "curve": [ 0, 6.27, 0.125, 3.78 ] + }, + { "time": 0.2667, "value": 3.78 } + ] + }, + "hip": { + "translate": [ + { + "x": -2.69, + "y": -6.79, + "curve": [ 0.067, -2.69, 0.2, -11.97, 0.067, -6.79, 0.2, -23.15 ] + }, + { "time": 0.2667, "x": -11.97, "y": -23.15 } + ] + }, + "front-fist": { + "rotate": [ + { + "value": -15.54, + "curve": [ 0, -3.08, 0.034, 18.44 ] + }, + { + "time": 0.0667, + "value": 19.02, + "curve": [ 0.108, 19.75, 0.169, 0 ] + }, + { "time": 0.2667 } + ], + "scale": [ + { + "x": 0.94, + "curve": [ 0, 0.962, 0.024, 1.237, 0, 1, 0.026, 0.947 ] + }, + { + "time": 0.0667, + "x": 1.236, + "y": 0.947, + "curve": [ 0.117, 1.235, 0.189, 1, 0.117, 0.947, 0.189, 1 ] + }, + { "time": 0.2667 } + ] + }, + "rear-bracer": { + "rotate": [ + { + "value": 11.75, + "curve": [ 0, -7.97, 0.017, -33.4 ] + }, + { + "time": 0.0333, + "value": -33.39, + "curve": [ 0.049, -33.37, 0.131, 0 ] + }, + { "time": 0.2 } + ] + }, + "torso": { + "rotate": [ + { + "value": -18.25, + "curve": [ 0, -10.59, 0.125, -22.88 ] + }, + { "time": 0.2667, "value": -22.88 } + ], + "scale": [ + { + "y": 1.03, + "curve": [ 0.067, 1, 0.132, 1, 0.067, 1.03, 0.132, 1 ] + }, + { "time": 0.2667 } + ] + }, + "head": { + "rotate": [ + { + "value": 5.12, + "curve": [ 0, -6.34, 0.125, -6.75 ] + }, + { "time": 0.2667, "value": -6.75 } + ], + "scale": [ + { + "y": 1.03, + "curve": [ 0.067, 1, 0.107, 1, 0.067, 1.03, 0.107, 1 ] + }, + { "time": 0.2667 } + ] + }, + "rear-foot-target": { + "translate": [ + { + "x": -58.39, + "y": 30.48, + "curve": [ 0, -7.15, 0.047, 16.62, 0, 12.71, 0.039, 0.22 ] + }, + { + "time": 0.1, + "x": 34.14, + "y": -0.19, + "curve": [ 0.136, 45.79, 0.163, 48.87, 0.133, -0.41, 0.163, 0 ] + }, + { "time": 0.2, "x": 48.87 } + ] + }, + "front-bracer": { + "rotate": [ + { + "value": 6.69, + "curve": [ 0, 19.76, 0.039, 56.53 ] + }, + { + "time": 0.0667, + "value": 56.63, + "curve": [ 0.114, 56.79, 0.189, 42.46 ] + }, + { "time": 0.2667, "value": 42.46 } + ] + }, + "front-foot-target": { + "rotate": [ + { + "value": -1.85, + "curve": [ 0.014, -8.91, 0.047, -28.4 ] + }, + { + "time": 0.1, + "value": -28.89, + "curve": [ 0.144, -29.29, 0.262, -21.77 ] + }, + { "time": 0.2667 } + ], + "translate": [ + { + "x": 9.97, + "y": 0.82, + "curve": [ 0, -54.41, 0.078, -69.06, 0, 0.15, 0.078, 0 ] + }, + { "time": 0.1667, "x": -69.06 } + ] + }, + "hair3": { + "rotate": [ + { + "value": -9.01, + "curve": [ 0.044, -9.01, 0.072, 7.41 ] + }, + { + "time": 0.1333, + "value": 10.08, + "curve": [ 0.166, 11.47, 0.208, 0 ] + }, + { "time": 0.2667 } + ] + }, + "hair4": { + "rotate": [ + { + "value": -16.49, + "curve": [ 0.044, -16.49, 0.101, -5.98 ] + }, + { + "time": 0.1333, + "value": -2.95, + "curve": [ 0.162, -0.34, 0.208, 0 ] + }, + { "time": 0.2667 } + ] + }, + "hair1": { + "rotate": [ + { + "value": -3.85, + "curve": [ 0.044, -3.85, 0.072, 6.91 ] + }, + { + "time": 0.1333, + "value": 8.05, + "curve": [ 0.166, 8.65, 0.208, 0 ] + }, + { "time": 0.2667 } + ] + }, + "hair2": { + "rotate": [ + { + "value": 1.25, + "curve": [ 0.044, 1.25, 0.072, 8.97 ] + }, + { + "time": 0.1333, + "value": 8.6, + "curve": [ 0.166, 8.4, 0.208, 0 ] + }, + { "time": 0.2667 } + ] + }, + "front-thigh": { + "translate": [ + { + "x": 12.21, + "y": 1.89, + "curve": [ 0.033, 12.21, 0.1, 0, 0.033, 1.89, 0.1, 0 ] + }, + { "time": 0.1333 } + ] + }, + "rear-thigh": { + "translate": [ + { + "x": -16.11, + "y": -1.38, + "curve": [ 0.033, -16.11, 0.1, 0, 0.033, -1.38, 0.1, 0 ] + }, + { "time": 0.1333 } + ] + }, + "torso3": { + "rotate": [ + { "time": 0.2667, "value": -2.15 } + ] + }, + "head-control": { + "translate": [ + { + "x": -13.72, + "y": -34.7, + "curve": [ 0.067, -13.72, 0.2, 0, 0.067, -34.7, 0.2, 0 ] + }, + { "time": 0.2667 } + ] + }, + "front-shoulder": { + "translate": [ + { + "x": 1.13, + "y": -14.31, + "curve": [ 0.067, 1.13, 0.2, 0, 0.067, -14.31, 0.2, 0 ] + }, + { "time": 0.2667 } + ] + } + } + }, + "jump": { + "slots": { + "front-fist": { + "attachment": [ + { "name": "front-fist-open" }, + { "time": 0.1, "name": "front-fist-closed" }, + { "time": 0.8333, "name": "front-fist-open" } + ] + } + }, + "bones": { + "front-thigh": { + "rotate": [ + { + "value": 55.08, + "curve": [ 0.007, 46.66, 0.043, 26.3 ] + }, + { + "time": 0.0667, + "value": 22.84, + "curve": [ 0.1, 17.99, 0.165, 15.78 ] + }, + { + "time": 0.2333, + "value": 15.71, + "curve": [ 0.309, 15.63, 0.408, 46.67 ] + }, + { + "time": 0.5, + "value": 63.6, + "curve": [ 0.56, 74.72, 0.762, 91.48 ] + }, + { + "time": 0.9667, + "value": 91.81, + "curve": [ 1.068, 92.01, 1.096, 22.05 ] + }, + { + "time": 1.1667, + "value": 22.25, + "curve": [ 1.18, 22.29, 1.176, 56.17 ] + }, + { + "time": 1.2, + "value": 56.16, + "curve": [ 1.246, 56.15, 1.263, 54.94 ] + }, + { "time": 1.3333, "value": 55.08 } + ], + "translate": [ + { "x": -5.13, "y": 11.55 } + ] + }, + "torso": { + "rotate": [ + { + "value": -45.57, + "curve": [ 0.022, -44.61, 0.03, -39.06 ] + }, + { + "time": 0.0667, + "value": -35.29, + "curve": [ 0.12, -29.77, 0.28, -19.95 ] + }, + { + "time": 0.4333, + "value": -19.95, + "curve": [ 0.673, -19.95, 0.871, -22.38 ] + }, + { + "time": 0.9667, + "value": -27.08, + "curve": [ 1.094, -33.33, 1.176, -44.93 ] + }, + { "time": 1.3333, "value": -45.57 } + ], + "translate": [ + { "x": -3.79, "y": -0.77 } + ] + }, + "rear-thigh": { + "rotate": [ + { + "value": 12.81, + "curve": [ 0.067, 12.81, 0.242, 67.88 ] + }, + { + "time": 0.2667, + "value": 74.11, + "curve": [ 0.314, 86.02, 0.454, 92.23 ] + }, + { + "time": 0.5667, + "value": 92.24, + "curve": [ 0.753, 92.26, 0.966, 67.94 ] + }, + { + "time": 1, + "value": 61.32, + "curve": [ 1.039, 53.75, 1.218, 12.68 ] + }, + { "time": 1.3333, "value": 12.81 } + ] + }, + "rear-shin": { + "rotate": [ + { + "value": -115.64, + "curve": [ 0.067, -117.17, 0.125, -117.15 ] + }, + { + "time": 0.1667, + "value": -117.15, + "curve": [ 0.225, -117.15, 0.332, -108.76 ] + }, + { + "time": 0.4, + "value": -107.15, + "curve": [ 0.48, -105.26, 0.685, -103.49 ] + }, + { + "time": 0.7667, + "value": -101.97, + "curve": [ 0.826, -100.87, 0.919, -92.3 ] + }, + { + "time": 1, + "value": -92.28, + "curve": [ 1.113, -92.26, 1.297, -114.22 ] + }, + { "time": 1.3333, "value": -115.64 } + ] + }, + "front-upper-arm": { + "rotate": [ + { + "value": -40.21, + "curve": [ 0.054, -35.46, 0.15, -31.12 ] + }, + { + "time": 0.2, + "value": -31.12, + "curve": [ 0.308, -31.12, 0.547, -80.12 ] + }, + { + "time": 0.6333, + "value": -96.56, + "curve": [ 0.697, -108.56, 0.797, -112.54 ] + }, + { + "time": 0.8667, + "value": -112.6, + "curve": [ 1.137, -112.84, 1.274, -49.19 ] + }, + { "time": 1.3333, "value": -40.21 } + ] + }, + "front-bracer": { + "rotate": [ + { + "value": 20.54, + "curve": [ 0.054, 32.23, 0.192, 55.84 ] + }, + { + "time": 0.2333, + "value": 62.58, + "curve": [ 0.29, 71.87, 0.375, 79.28 ] + }, + { + "time": 0.4333, + "value": 79.18, + "curve": [ 0.555, 78.98, 0.684, 27.54 ] + }, + { + "time": 0.7333, + "value": 13.28, + "curve": [ 0.786, -1.85, 0.874, -24.76 ] + }, + { + "time": 1, + "value": -25.45, + "curve": [ 1.165, -26.36, 1.303, 9.1 ] + }, + { "time": 1.3333, "value": 20.54 } + ] + }, + "front-fist": { + "rotate": [ + { + "value": -36.16, + "curve": [ 0.114, -39.59, 0.3, -45.61 ] + }, + { + "time": 0.4, + "value": -45.61, + "curve": [ 0.442, -45.61, 0.537, -21.54 ] + }, + { + "time": 0.5667, + "value": -15.4, + "curve": [ 0.592, -10.23, 0.692, 11.89 ] + }, + { + "time": 0.7333, + "value": 11.73, + "curve": [ 0.783, 11.54, 0.831, 1.8 ] + }, + { + "time": 0.8667, + "value": -5.78, + "curve": [ 0.897, -12.22, 0.901, -14.22 ] + }, + { + "time": 0.9333, + "value": -14.51, + "curve": [ 0.974, -14.89, 0.976, 10.38 ] + }, + { + "time": 1, + "value": 10.55, + "curve": [ 1.027, 10.74, 1.023, -8.44 ] + }, + { + "time": 1.0333, + "value": -8.42, + "curve": [ 1.059, -8.36, 1.074, 10.12 ] + }, + { + "time": 1.1, + "value": 10.22, + "curve": [ 1.168, 10.48, 1.27, -36.07 ] + }, + { "time": 1.3333, "value": -36.16 } + ] + }, + "rear-upper-arm": { + "rotate": [ + { + "value": 40.5, + "curve": [ 0.048, 36.1, 0.168, 20.45 ] + }, + { + "time": 0.3, + "value": 20.45, + "curve": [ 0.476, 20.45, 0.571, 33.76 ] + }, + { + "time": 0.6, + "value": 38.67, + "curve": [ 0.642, 45.8, 0.681, 57.44 ] + }, + { + "time": 0.7333, + "value": 62.91, + "curve": [ 0.829, 72.8, 0.996, 77.61 ] + }, + { + "time": 1.0333, + "value": 80.37, + "curve": [ 1.082, 83.94, 1.148, 90.6 ] + }, + { + "time": 1.2, + "value": 90.6, + "curve": [ 1.248, 90.46, 1.317, 53.07 ] + }, + { "time": 1.3333, "value": 49.06 } + ] + }, + "rear-bracer": { + "rotate": [ + { + "value": 28.28, + "curve": [ 0.022, 25.12, 0.187, -0.89 ] + }, + { + "time": 0.2, + "value": -2.52, + "curve": [ 0.257, -9.92, 0.372, -17.38 ] + }, + { + "time": 0.4333, + "value": -17.41, + "curve": [ 0.54, -17.47, 0.659, -16.91 ] + }, + { + "time": 0.7667, + "value": -12.1, + "curve": [ 0.907, -5.79, 1.025, 14.58 ] + }, + { + "time": 1.1, + "value": 20.58, + "curve": [ 1.191, 27.85, 1.283, 29.67 ] + }, + { "time": 1.3333, "value": 29.67 } + ] + }, + "neck": { + "rotate": [ + { + "value": 11.88, + "curve": [ 0.104, 11.82, 0.179, 11.15 ] + }, + { + "time": 0.2, + "value": 10.08, + "curve": [ 0.255, 7.29, 0.405, -8.15 ] + }, + { + "time": 0.4333, + "value": -9.35, + "curve": [ 0.508, -12.48, 0.595, -13.14 ] + }, + { + "time": 0.6667, + "value": -12.61, + "curve": [ 0.714, -12.26, 0.815, -5.57 ] + }, + { + "time": 0.8333, + "value": -4.08, + "curve": [ 0.883, -0.07, 1.045, 12.77 ] + }, + { + "time": 1.1, + "value": 15.06, + "curve": [ 1.208, 19.6, 1.279, 20.64 ] + }, + { "time": 1.3333, "value": 20.73 } + ] + }, + "head": { + "rotate": [ + { + "value": 13.14, + "curve": [ 0.008, 12.19, 0.197, -23.53 ] + }, + { + "time": 0.3333, + "value": -23.95, + "curve": [ 0.509, -23.95, 0.667, -2.66 ] + }, + { + "time": 0.7333, + "value": -2.66, + "curve": [ 0.792, -2.66, 0.908, -13.32 ] + }, + { + "time": 0.9667, + "value": -13.32, + "curve": [ 1.158, -13.11, 1.241, -1.58 ] + }, + { "time": 1.3333, "value": -1.58 } + ], + "scale": [ + { + "curve": [ 0.041, 1, 0.052, 0.962, 0.041, 1, 0.052, 1.137 ] + }, + { + "time": 0.1, + "x": 0.954, + "y": 1.137, + "curve": [ 0.202, 0.962, 0.318, 1, 0.202, 1.137, 0.252, 1.002 ] + }, + { "time": 0.4667 }, + { + "time": 1.0667, + "x": 1.002, + "curve": [ 1.092, 1.002, 1.126, 1.143, 1.092, 1, 1.128, 0.975 ] + }, + { + "time": 1.1667, + "x": 1.144, + "y": 0.973, + "curve": [ 1.204, 1.145, 1.233, 0.959, 1.206, 0.972, 1.227, 1.062 ] + }, + { + "time": 1.2667, + "x": 0.958, + "y": 1.063, + "curve": [ 1.284, 0.958, 1.292, 1.001, 1.288, 1.063, 1.288, 1.001 ] + }, + { "time": 1.3333 } + ] + }, + "hip": { + "translate": [ + { + "y": -45.46, + "curve": [ 0.042, -0.09, 0.15, 15.22, 0.031, 44.98, 0.123, 289.73 ] + }, + { + "time": 0.2, + "x": 15.22, + "y": 415.85, + "curve": [ 0.332, 15.22, 0.539, -34.52, 0.271, 532.93, 0.483, 720.5 ] + }, + { + "time": 0.7667, + "x": -34.52, + "y": 721.6, + "curve": [ 0.888, -34.52, 1.057, -21.95, 1.049, 721.17, 1.098, 379.84 ] + }, + { + "time": 1.1333, + "x": -15.67, + "y": 266.77, + "curve": [ 1.144, -14.77, 1.188, -10.53, 1.15, 213.72, 1.172, -61.32 ] + }, + { + "time": 1.2333, + "x": -6.53, + "y": -61.34, + "curve": [ 1.272, -3.22, 1.311, 0.05, 1.291, -61.36, 1.296, -44.8 ] + }, + { "time": 1.3333, "y": -45.46 } + ] + }, + "front-shin": { + "rotate": [ + { + "value": -74.19, + "curve": [ 0, -51.14, 0.042, -12.54 ] + }, + { + "time": 0.1667, + "value": -12.28, + "curve": [ 0.285, -12.32, 0.37, -74.44 ] + }, + { + "time": 0.4333, + "value": -92.92, + "curve": [ 0.498, -111.86, 0.617, -140.28 ] + }, + { + "time": 0.9, + "value": -140.84, + "curve": [ 1.004, -141.04, 1.09, -47.87 ] + }, + { + "time": 1.1, + "value": -37.44, + "curve": [ 1.108, -29.83, 1.14, -21.18 ] + }, + { + "time": 1.1667, + "value": -21.08, + "curve": [ 1.18, -21.03, 1.191, -50.65 ] + }, + { + "time": 1.2, + "value": -53.17, + "curve": [ 1.22, -58.53, 1.271, -73.38 ] + }, + { "time": 1.3333, "value": -74.19 } + ] + }, + "front-foot": { + "rotate": [ + { + "value": 7.35, + "curve": [ 0, 4.8, 0.05, -26.64 ] + }, + { + "time": 0.0667, + "value": -26.64, + "curve": [ 0.192, -26.64, 0.442, -11.77 ] + }, + { + "time": 0.5667, + "value": -11.77, + "curve": [ 0.692, -11.77, 0.942, -19.36 ] + }, + { + "time": 1.0667, + "value": -19.36, + "curve": [ 1.133, -19.36, 1.32, 3.82 ] + }, + { "time": 1.3333, "value": 7.35 } + ] + }, + "rear-foot": { + "rotate": [ + { "value": -7.14 } + ] + }, + "gun": { + "rotate": [ + { + "value": 12.36, + "curve": [ 0.022, 16.28, 0.15, 30.81 ] + }, + { + "time": 0.2, + "value": 30.81, + "curve": [ 0.258, 30.81, 0.375, 13.26 ] + }, + { + "time": 0.4333, + "value": 13.26, + "curve": [ 0.508, 13.26, 0.658, 15.05 ] + }, + { + "time": 0.7333, + "value": 14.98, + "curve": [ 0.789, 14.94, 0.828, 13.62 ] + }, + { + "time": 0.8667, + "value": 12.72, + "curve": [ 0.887, 12.25, 0.984, 9.83 ] + }, + { + "time": 1.0333, + "value": 8.6, + "curve": [ 1.045, 8.31, 1.083, 7.55 ] + }, + { + "time": 1.1333, + "value": 7.13, + "curve": [ 1.175, 6.78, 1.283, 6.18 ] + }, + { "time": 1.3333, "value": 6.18 } + ] + }, + "front-leg-target": { + "translate": [ + { "x": -13.95, "y": -30.34 } + ] + }, + "rear-leg-target": { + "rotate": [ + { "value": -38.43 } + ], + "translate": [ + { "x": 85, "y": -33.59 } + ] + }, + "front-foot-target": { + "rotate": [ + { "value": -62.54 } + ], + "translate": [ + { "x": 16.34, "y": 0.18 } + ] + }, + "rear-foot-target": { + "rotate": [ + { "value": 18.55 } + ], + "translate": [ + { "x": -176.39, "y": 134.12 } + ] + }, + "back-foot-tip": { + "rotate": [ + { + "value": -143.73, + "curve": [ 0.083, -144.24, 0.167, -74.26 ] + }, + { + "time": 0.2667, + "value": -52.76, + "curve": [ 0.342, -36.57, 0.513, -36.57 ] + }, + { + "time": 0.6333, + "value": -30.97, + "curve": [ 0.724, -26.78, 0.848, -17.06 ] + }, + { + "time": 0.9667, + "value": -16.74, + "curve": [ 1.167, -16.2, 1.272, -144.17 ] + }, + { "time": 1.3333, "value": -143.73 } + ] + }, + "front-foot-tip": { + "rotate": [ + { + "value": -1.57, + "curve": [ 0, -24.71, 0.162, -60.88 ] + }, + { + "time": 0.2667, + "value": -60.83, + "curve": [ 0.342, -60.8, 0.582, -43.5 ] + }, + { + "time": 0.7, + "value": -39.45, + "curve": [ 0.773, -36.94, 0.832, -36.78 ] + }, + { + "time": 0.9667, + "value": -36.6, + "curve": [ 1.054, -36.49, 1.092, -37.37 ] + }, + { + "time": 1.1667, + "value": -33.26, + "curve": [ 1.237, -29.37, 1.147, -1.41 ] + }, + { "time": 1.2, "value": -1.57 } + ] + }, + "hair3": { + "rotate": [ + { + "value": -6.81, + "curve": [ 0, 13.59, 0.117, 18.21 ] + }, + { + "time": 0.1333, + "value": 18.21, + "curve": [ 0.167, 18.21, 0.26, 12.95 ] + }, + { + "time": 0.3, + "value": 11.56, + "curve": [ 0.382, 8.7, 0.55, 9.43 ] + }, + { + "time": 0.6667, + "value": 9.32, + "curve": [ 0.843, 9.15, 0.918, -7.34 ] + }, + { "time": 1.3333, "value": -6.81 } + ], + "translate": [ + { + "time": 0.6667, + "curve": [ 0.781, 0, 0.972, 16.03, 0.781, 0, 0.972, 0.92 ] + }, + { + "time": 1.1333, + "x": 16.03, + "y": 0.92, + "curve": [ 1.211, 16.03, 1.281, 0, 1.211, 0.92, 1.281, 0 ] + }, + { "time": 1.3333 } + ] + }, + "hair4": { + "rotate": [ + { + "value": -6.81, + "curve": [ 0.001, -3.88, 0.063, 16.18 ] + }, + { + "time": 0.1667, + "value": 16.14, + "curve": [ 0.242, 16.1, 0.249, 16.07 ] + }, + { + "time": 0.3333, + "value": 13.46, + "curve": [ 0.442, 10.09, 0.573, -2.2 ] + }, + { + "time": 0.6, + "value": -6.04, + "curve": [ 0.614, -8.05, 0.717, -33.44 ] + }, + { + "time": 0.7667, + "value": -33.44, + "curve": [ 0.809, -33.44, 0.835, -31.32 ] + }, + { + "time": 0.8667, + "value": -27.36, + "curve": [ 0.874, -26.47, 0.903, -14.28 ] + }, + { + "time": 0.9333, + "value": -14.47, + "curve": [ 0.956, -14.62, 0.944, -25.91 ] + }, + { + "time": 1, + "value": -25.96, + "curve": [ 1.062, -26.02, 1.051, -1.87 ] + }, + { + "time": 1.0667, + "value": -1.87, + "curve": [ 1.096, -1.87, 1.096, -16.09 ] + }, + { + "time": 1.1333, + "value": -16.08, + "curve": [ 1.169, -16.08, 1.153, -3.38 ] + }, + { + "time": 1.2, + "value": -3.38, + "curve": [ 1.234, -3.38, 1.271, -6.07 ] + }, + { "time": 1.3333, "value": -6.07 } + ] + }, + "hair2": { + "rotate": [ + { + "value": -6.81, + "curve": [ 0, -3.17, 0.042, 16.33 ] + }, + { + "time": 0.0667, + "value": 16.33, + "curve": [ 0.21, 15.74, 0.208, -12.06 ] + }, + { + "time": 0.3333, + "value": -12.21, + "curve": [ 0.417, -12.3, 0.552, -3.98 ] + }, + { + "time": 0.6667, + "value": 1.52, + "curve": [ 0.726, 4.35, 0.817, 4.99 ] + }, + { + "time": 0.8667, + "value": 4.99, + "curve": [ 0.901, 4.99, 0.912, -29.05 ] + }, + { + "time": 0.9667, + "value": -27.45, + "curve": [ 0.987, -26.83, 1.018, -5.42 ] + }, + { + "time": 1.0667, + "value": -5.46, + "curve": [ 1.107, -5.22, 1.095, -33.51 ] + }, + { + "time": 1.1333, + "value": -33.28, + "curve": [ 1.162, -33.57, 1.192, 8.04 ] + }, + { + "time": 1.2667, + "value": 7.86, + "curve": [ 1.302, 7.77, 1.313, 2.7 ] + }, + { "time": 1.3333, "value": 2.7 } + ] + }, + "hair1": { + "rotate": [ + { + "value": -6.81, + "curve": [ 0.001, -3.12, 0.074, 14.66 ] + }, + { + "time": 0.1333, + "value": 14.66, + "curve": [ 0.188, 14.8, 0.293, 9.56 ] + }, + { + "time": 0.3333, + "value": 5.99, + "curve": [ 0.381, 1.72, 0.55, -11.11 ] + }, + { + "time": 0.6667, + "value": -11.11, + "curve": [ 0.833, -11.11, 0.933, 22.54 ] + }, + { + "time": 1.1, + "value": 22.54, + "curve": [ 1.158, 22.54, 1.275, -6.81 ] + }, + { "time": 1.3333, "value": -6.81 } + ] + }, + "torso2": { + "rotate": [ + { + "value": 4.52, + "curve": [ 0.013, 2.33, 0.092, -9.75 ] + }, + { + "time": 0.1333, + "value": -9.75, + "curve": [ 0.175, -9.75, 0.291, -1.26 ] + }, + { + "time": 0.3333, + "value": 0.96, + "curve": [ 0.359, 2.3, 0.543, 4.25 ] + }, + { + "time": 0.6, + "value": 4.68, + "curve": [ 0.683, 5.3, 0.771, 5.92 ] + }, + { + "time": 0.8333, + "value": 6.48, + "curve": [ 0.871, 6.82, 1.083, 11.37 ] + }, + { + "time": 1.1667, + "value": 11.37, + "curve": [ 1.208, 11.37, 1.317, 6.18 ] + }, + { "time": 1.3333, "value": 4.52 } + ], + "translate": [ + { + "curve": [ 0, 0, 0.082, -2.24, 0, 0, 0.082, -0.42 ] + }, + { + "time": 0.1667, + "x": -2.98, + "y": -0.56, + "curve": [ 0.232, -2.24, 0.298, 0, 0.232, -0.42, 0.298, 0 ] + }, + { "time": 0.3333, "curve": "stepped" }, + { + "time": 0.8667, + "curve": [ 0.889, 0, 0.912, 0.26, 0.889, 0, 0.912, 0.06 ] + }, + { + "time": 0.9333, + "x": 0.68, + "y": 0.23, + "curve": [ 1.016, 2.22, 1.095, 5.9, 1.023, 0.97, 1.095, 1.99 ] + }, + { + "time": 1.1667, + "x": 6.47, + "y": 2.18, + "curve": [ 1.23, 5.75, 1.286, 0, 1.23, 1.94, 1.286, 0 ] + }, + { "time": 1.3333 } + ] + }, + "torso3": { + "rotate": [ + { + "value": 4.52, + "curve": [ 0.025, 4.52, 0.075, -6.17 ] + }, + { + "time": 0.1, + "value": -6.17, + "curve": [ 0.175, -6.17, 0.381, -0.71 ] + }, + { + "time": 0.4, + "value": -0.25, + "curve": [ 0.447, 0.87, 0.775, 4.84 ] + }, + { + "time": 0.9, + "value": 4.84, + "curve": [ 1.008, 4.84, 1.225, 4.52 ] + }, + { "time": 1.3333, "value": 4.52 } + ] + }, + "head-control": { + "translate": [ + { + "curve": [ 0.138, -2.4, 0.227, -10.44, 0.123, 1.05, 0.227, 2.7 ] + }, + { + "time": 0.3667, + "x": -10.44, + "y": 2.7, + "curve": [ 0.484, -10.44, 0.585, -5.63, 0.484, 2.7, 0.629, -23.62 ] + }, + { + "time": 0.7333, + "x": -2.29, + "y": -26.61, + "curve": [ 0.818, -0.39, 0.962, 1.21, 0.858, -30.17, 0.972, -28.75 ] + }, + { + "time": 1.1, + "x": 1.25, + "y": -28.75, + "curve": [ 1.192, 1.28, 1.234, 0.98, 1.224, -28.75, 1.235, -2.15 ] + }, + { "time": 1.3333 } + ] + }, + "front-shoulder": { + "translate": [ + { + "curve": [ 0.031, -2.22, 0.065, -3.73, 0.02, -3.25, 0.065, -14.74 ] + }, + { + "time": 0.1, + "x": -3.73, + "y": -14.74, + "curve": [ 0.216, -3.73, 0.384, -0.17, 0.216, -14.74, 0.402, -12.51 ] + }, + { + "time": 0.5, + "x": 1.63, + "y": -9.51, + "curve": [ 0.632, 3.69, 0.935, 7.41, 0.585, -6.91, 0.909, 10.86 ] + }, + { + "time": 1.1, + "x": 7.45, + "y": 10.99, + "curve": [ 1.18, 7.46, 1.265, 2.86, 1.193, 11.05, 1.294, 3.38 ] + }, + { "time": 1.3333 } + ] + } + }, + "ik": { + "front-foot-ik": [ + { + "mix": 0, + "curve": [ 0.3, 0, 0.9, 1, 0.3, 0, 0.9, 0 ] + }, + { "time": 1.2 } + ], + "front-leg-ik": [ + { + "mix": 0, + "bendPositive": false, + "curve": [ 0.3, 0, 0.9, 1, 0.3, 0, 0.9, 0 ] + }, + { "time": 1.2, "bendPositive": false } + ], + "rear-foot-ik": [ + { "mix": 0 } + ], + "rear-leg-ik": [ + { "mix": 0, "bendPositive": false } + ] + }, + "events": [ + { "time": 1.2, "name": "footstep" } + ] + }, + "portal": { + "slots": { + "clipping": { + "attachment": [ + { "name": "clipping" } + ] + }, + "front-fist": { + "attachment": [ + { "name": "front-fist-open" } + ] + }, + "mouth": { + "attachment": [ + { "time": 0.9, "name": "mouth-grind" }, + { "time": 2.2667, "name": "mouth-smile" } + ] + }, + "portal-bg": { + "attachment": [ + { "name": "portal-bg" }, + { "time": 3 } + ] + }, + "portal-flare1": { + "attachment": [ + { "time": 1.1, "name": "portal-flare1" }, + { "time": 1.1333, "name": "portal-flare2" }, + { "time": 1.1667, "name": "portal-flare3" }, + { "time": 1.2, "name": "portal-flare1" }, + { "time": 1.2333, "name": "portal-flare2" }, + { "time": 1.2667, "name": "portal-flare1" }, + { "time": 1.3333 } + ] + }, + "portal-flare2": { + "attachment": [ + { "time": 1.1, "name": "portal-flare2" }, + { "time": 1.1333, "name": "portal-flare3" }, + { "time": 1.1667, "name": "portal-flare1" }, + { "time": 1.2, "name": "portal-flare2" }, + { "time": 1.2333, "name": "portal-flare3" }, + { "time": 1.2667 } + ] + }, + "portal-flare3": { + "attachment": [ + { "time": 1.2, "name": "portal-flare3" }, + { "time": 1.2333, "name": "portal-flare2" }, + { "time": 1.2667 } + ] + }, + "portal-flare4": { + "attachment": [ + { "time": 1.2, "name": "portal-flare2" }, + { "time": 1.2333, "name": "portal-flare1" }, + { "time": 1.2667, "name": "portal-flare2" }, + { "time": 1.3333 } + ] + }, + "portal-flare5": { + "attachment": [ + { "time": 1.2333, "name": "portal-flare3" }, + { "time": 1.2667, "name": "portal-flare1" }, + { "time": 1.3333 } + ] + }, + "portal-flare6": { + "attachment": [ + { "time": 1.2667, "name": "portal-flare3" }, + { "time": 1.3333 } + ] + }, + "portal-flare7": { + "attachment": [ + { "time": 1.1333, "name": "portal-flare2" }, + { "time": 1.1667 } + ] + }, + "portal-flare8": { + "attachment": [ + { "time": 1.2, "name": "portal-flare3" }, + { "time": 1.2333, "name": "portal-flare2" }, + { "time": 1.2667 } + ] + }, + "portal-flare9": { + "attachment": [ + { "time": 1.2, "name": "portal-flare2" }, + { "time": 1.2333, "name": "portal-flare3" }, + { "time": 1.2667, "name": "portal-flare1" }, + { "time": 1.3 } + ] + }, + "portal-flare10": { + "attachment": [ + { "time": 1.2, "name": "portal-flare2" }, + { "time": 1.2333, "name": "portal-flare1" }, + { "time": 1.2667, "name": "portal-flare3" }, + { "time": 1.3 } + ] + }, + "portal-shade": { + "attachment": [ + { "name": "portal-shade" }, + { "time": 3 } + ] + }, + "portal-streaks1": { + "attachment": [ + { "name": "portal-streaks1" }, + { "time": 3 } + ] + }, + "portal-streaks2": { + "attachment": [ + { "name": "portal-streaks2" }, + { "time": 3 } + ] + } + }, + "bones": { + "portal-root": { + "translate": [ + { + "x": -458.35, + "y": 105.19, + "curve": [ 0.333, -458.22, 0.669, -457.86, 0.934, 105.19, 0.671, 105.19 ] + }, + { + "time": 1, + "x": -456.02, + "y": 105.19, + "curve": [ 1.339, -454.14, 2.208, -447.28, 1.35, 105.19, 2.05, 105.19 ] + }, + { + "time": 2.4, + "x": -439.12, + "y": 105.19, + "curve": [ 2.463, -436.44, 2.502, -432.92, 2.487, 105.19, 2.512, 105.09 ] + }, + { + "time": 2.6, + "x": -432.58, + "y": 105.09, + "curve": [ 2.784, -431.94, 2.978, -446.6, 2.772, 105.09, 2.933, 105.19 ] + }, + { "time": 3.0333, "x": -457.42, "y": 105.19 } + ], + "scale": [ + { + "x": 0.003, + "y": 0.006, + "curve": [ 0.329, 0.044, 0.347, 0.117, 0.329, 0.097, 0.37, 0.249 ] + }, + { + "time": 0.4, + "x": 0.175, + "y": 0.387, + "curve": [ 0.63, 0.619, 0.663, 0.723, 0.609, 1.338, 0.645, 1.524 ] + }, + { + "time": 0.7333, + "x": 0.724, + "y": 1.52, + "curve": [ 0.798, 0.725, 0.907, 0.647, 0.797, 1.517, 0.895, 1.424 ] + }, + { + "time": 1, + "x": 0.645, + "y": 1.426, + "curve": [ 1.095, 0.643, 1.139, 0.688, 1.089, 1.428, 1.115, 1.513 ] + }, + { + "time": 1.2333, + "x": 0.685, + "y": 1.516, + "curve": [ 1.325, 0.683, 1.508, 0.636, 1.343, 1.518, 1.467, 1.4 ] + }, + { + "time": 1.6, + "x": 0.634, + "y": 1.401, + "curve": [ 1.728, 0.631, 1.946, 0.687, 1.722, 1.402, 1.924, 1.522 ] + }, + { + "time": 2.0667, + "x": 0.688, + "y": 1.522, + "curve": [ 2.189, 0.69, 2.289, 0.649, 2.142, 1.522, 2.265, 1.417 ] + }, + { + "time": 2.4, + "x": 0.65, + "y": 1.426, + "curve": [ 2.494, 0.651, 2.504, 0.766, 2.508, 1.434, 2.543, 1.566 ] + }, + { + "time": 2.6, + "x": 0.766, + "y": 1.568, + "curve": [ 2.73, 0.765, 3.006, 0.098, 2.767, 1.564, 2.997, 0.1 ] + }, + { "time": 3.0333, "x": 0.007, "y": 0.015 } + ] + }, + "portal-streaks1": { + "rotate": [ + {}, + { "time": 3.1667, "value": 1200 } + ], + "translate": [ + { + "x": 15.15, + "curve": [ 0.162, 15.15, 0.432, 12.6, 0.162, 0, 0.432, -3.86 ] + }, + { + "time": 0.6667, + "x": 10.9, + "y": -6.44, + "curve": [ 0.794, 9.93, 0.912, 9.21, 0.794, -7.71, 0.912, -8.66 ] + }, + { + "time": 1, + "x": 9.21, + "y": -8.66, + "curve": [ 1.083, 9.21, 1.25, 21.53, 1.083, -8.66, 1.265, -4.9 ] + }, + { + "time": 1.3333, + "x": 21.53, + "y": -3.19, + "curve": [ 1.5, 21.53, 1.939, 12.3, 1.446, -0.37, 1.9, 6.26 ] + }, + { + "time": 2.0667, + "x": 11.26, + "y": 6.26, + "curve": [ 2.239, 9.85, 2.389, 9.68, 2.208, 6.26, 2.523, 0.51 ] + }, + { + "time": 2.5667, + "x": 9.39, + "y": -0.8, + "curve": [ 2.657, 9.24, 2.842, 9.21, 2.646, -3.2, 2.842, -8.91 ] + }, + { "time": 2.9333, "x": 9.21, "y": -8.91 } + ], + "scale": [ + { + "curve": [ 0.167, 1, 0.5, 1.053, 0.167, 1, 0.5, 1.053 ] + }, + { + "time": 0.6667, + "x": 1.053, + "y": 1.053, + "curve": [ 0.833, 1.053, 1.167, 0.986, 0.833, 1.053, 1.167, 0.986 ] + }, + { + "time": 1.3333, + "x": 0.986, + "y": 0.986, + "curve": [ 1.5, 0.986, 1.833, 1.053, 1.5, 0.986, 1.833, 1.053 ] + }, + { "time": 2, "x": 1.053, "y": 1.053 } + ] + }, + "portal-streaks2": { + "rotate": [ + {}, + { "time": 3.1667, "value": 600 } + ], + "translate": [ + { "x": -2.11 }, + { "time": 1, "x": -2.11, "y": 6.63 }, + { "time": 1.9333, "x": -2.11 } + ], + "scale": [ + { + "x": 1.014, + "y": 1.014, + "curve": [ 0.229, 0.909, 0.501, 0.755, 0.242, 0.892, 0.502, 0.768 ] + }, + { + "time": 0.8667, + "x": 0.745, + "y": 0.745, + "curve": [ 1.282, 0.733, 2.021, 0.699, 1.27, 0.719, 2.071, 0.709 ] + }, + { + "time": 2.2, + "x": 0.7, + "y": 0.704, + "curve": [ 2.315, 0.7, 2.421, 0.794, 2.311, 0.701, 2.485, 0.797 ] + }, + { + "time": 2.5667, + "x": 0.794, + "y": 0.794, + "curve": [ 2.734, 0.794, 2.99, 0.323, 2.714, 0.789, 3.019, 0.341 ] + }, + { "time": 3.1667, "x": 0, "y": 0 } + ] + }, + "portal-shade": { + "translate": [ + { "x": -29.68 } + ], + "scale": [ + { "x": 0.714, "y": 0.714 } + ] + }, + "portal": { + "rotate": [ + {}, + { "time": 3.1667, "value": 600 } + ] + }, + "clipping": { + "translate": [ + { "x": -476.55, "y": 2.27 } + ], + "scale": [ + { "x": 0.983, "y": 1.197 } + ] + }, + "hip": { + "rotate": [ + { + "time": 1.0667, + "value": 22.74, + "curve": [ 1.163, 18.84, 1.77, 8.77 ] + }, + { + "time": 1.9, + "value": 7.82, + "curve": [ 2.271, 5.1, 2.89, 0 ] + }, + { "time": 3.1667 } + ], + "translate": [ + { "x": -899.41, "y": 4.47, "curve": "stepped" }, + { + "time": 1.0667, + "x": -694.16, + "y": 183.28, + "curve": [ 1.091, -602.08, 1.138, -427.59, 1.115, 185.6, 1.171, 133.18 ] + }, + { + "time": 1.2333, + "x": -316.97, + "y": 55.29, + "curve": [ 1.317, -220.27, 1.512, -123.21, 1.271, 8.68, 1.461, -83.18 ] + }, + { + "time": 1.6, + "x": -95.53, + "y": -112.23, + "curve": [ 1.718, -58.25, 2.037, -22.54, 1.858, -166.17, 2.109, -31.4 ] + }, + { + "time": 2.1667, + "x": -14.82, + "y": -31.12, + "curve": [ 2.294, -7.28, 2.442, -7.2, 2.274, -30.6, 2.393, -36.76 ] + }, + { + "time": 2.6, + "x": -7.2, + "y": -36.96, + "curve": [ 2.854, -7.2, 3.071, -11.87, 2.786, -36.27, 3.082, -22.98 ] + }, + { "time": 3.1667, "x": -11.97, "y": -23.15 } + ] + }, + "rear-foot-target": { + "rotate": [ + { "time": 1.0667, "value": 41.6, "curve": "stepped" }, + { + "time": 1.2333, + "value": 41.6, + "curve": [ 1.258, 41.6, 1.379, 35.46 ] + }, + { + "time": 1.4, + "value": 30.09, + "curve": [ 1.412, 27.04, 1.433, 10.65 ] + }, + { "time": 1.4333, "value": -0.28 }, + { "time": 1.6, "value": 2.44 } + ], + "translate": [ + { "x": -899.41, "y": 4.47, "curve": "stepped" }, + { + "time": 1.0667, + "x": -591.13, + "y": 438.46, + "curve": [ 1.076, -539.77, 1.206, -268.1, 1.117, 418.44, 1.21, 333.18 ] + }, + { + "time": 1.2333, + "x": -225.28, + "y": 304.53, + "curve": [ 1.265, -175.22, 1.393, -74.21, 1.296, 226.52, 1.401, 49.61 ] + }, + { + "time": 1.4333, + "x": -52.32, + "y": 0.2, + "curve": [ 1.454, -40.85, 1.616, 40.87, 1.466, 0.17, 1.614, 0.04 ] + }, + { "time": 1.6667, "x": 45.87, "y": 0.01 }, + { "time": 1.9333, "x": 48.87 } + ] + }, + "front-foot-target": { + "rotate": [ + { + "time": 1.0667, + "value": 32.08, + "curve": [ 1.108, 32.08, 1.192, 35.16 ] + }, + { + "time": 1.2333, + "value": 35.16, + "curve": [ 1.258, 35.16, 1.317, 2.23 ] + }, + { + "time": 1.3333, + "value": -4.74, + "curve": [ 1.351, -12.14, 1.429, -34.96 ] + }, + { + "time": 1.6, + "value": -34.77, + "curve": [ 1.765, -34.58, 1.897, -17.25 ] + }, + { "time": 1.9333 } + ], + "translate": [ + { "x": -899.41, "y": 4.47, "curve": "stepped" }, + { + "time": 1.0667, + "x": -533.93, + "y": 363.75, + "curve": [ 1.074, -480.85, 1.18, -261.31, 1.094, 362.3, 1.195, 267.77 ] + }, + { + "time": 1.2333, + "x": -201.23, + "y": 199.93, + "curve": [ 1.269, -161.38, 1.294, -140.32, 1.274, 126.67, 1.308, 77.12 ] + }, + { + "time": 1.3333, + "x": -124.08, + "y": 0.2, + "curve": [ 1.426, -85.6, 1.633, -69.06, 1.45, 0.48, 1.633, 0 ] + }, + { "time": 1.7333, "x": -69.06 } + ] + }, + "torso": { + "rotate": [ + { + "time": 1.0667, + "value": 27.02, + "curve": [ 1.187, 26.86, 1.291, 7.81 ] + }, + { + "time": 1.3333, + "value": -2.62, + "curve": [ 1.402, -19.72, 1.429, -48.64 ] + }, + { + "time": 1.4667, + "value": -56.31, + "curve": [ 1.509, -64.87, 1.62, -77.14 ] + }, + { + "time": 1.7333, + "value": -77.34, + "curve": [ 1.837, -76.89, 1.895, -71.32 ] + }, + { + "time": 2, + "value": -57.52, + "curve": [ 2.104, -43.83, 2.189, -28.59 ] + }, + { + "time": 2.3, + "value": -29.03, + "curve": [ 2.413, -29.48, 2.513, -36.79 ] + }, + { + "time": 2.6667, + "value": -36.79, + "curve": [ 2.814, -36.95, 2.947, -22.88 ] + }, + { "time": 3.1667, "value": -22.88 } + ] + }, + "neck": { + "rotate": [ + { + "time": 1.0667, + "value": -3.57, + "curve": [ 1.146, -3.66, 1.15, -13.5 ] + }, + { + "time": 1.2333, + "value": -13.5, + "curve": [ 1.428, -13.5, 1.443, 11.58 ] + }, + { + "time": 1.5667, + "value": 11.42, + "curve": [ 1.658, 11.3, 1.775, 3.78 ] + }, + { + "time": 1.8667, + "value": 3.78, + "curve": [ 1.92, 3.78, 2.036, 8.01 ] + }, + { + "time": 2.1, + "value": 7.93, + "curve": [ 2.266, 7.72, 2.42, 3.86 ] + }, + { + "time": 2.5333, + "value": 3.86, + "curve": [ 2.783, 3.86, 3.004, 3.78 ] + }, + { "time": 3.1667, "value": 3.78 } + ] + }, + "head": { + "rotate": [ + { + "time": 1.0667, + "value": 16.4, + "curve": [ 1.133, 9.9, 1.207, 1.87 ] + }, + { + "time": 1.3333, + "value": 1.67, + "curve": [ 1.46, 1.56, 1.547, 47.54 ] + }, + { + "time": 1.7333, + "value": 47.55, + "curve": [ 1.897, 47.56, 2.042, 5.68 ] + }, + { + "time": 2.0667, + "value": 0.86, + "curve": [ 2.074, -0.61, 2.086, -2.81 ] + }, + { + "time": 2.1, + "value": -5.31, + "curve": [ 2.145, -13.07, 2.216, -23.65 ] + }, + { + "time": 2.2667, + "value": -23.71, + "curve": [ 2.334, -23.79, 2.426, -13.43 ] + }, + { + "time": 2.4667, + "value": -9.18, + "curve": [ 2.498, -5.91, 2.604, 2.53 ] + }, + { + "time": 2.6667, + "value": 2.52, + "curve": [ 2.738, 2.24, 2.85, -8.76 ] + }, + { + "time": 2.9333, + "value": -8.67, + "curve": [ 3.036, -8.55, 3.09, -7.09 ] + }, + { "time": 3.1667, "value": -6.75 } + ], + "scale": [ + { + "time": 1.3333, + "curve": [ 1.392, 1, 1.526, 1, 1.392, 1, 1.508, 1.043 ] + }, + { + "time": 1.5667, + "x": 0.992, + "y": 1.043, + "curve": [ 1.598, 0.985, 1.676, 0.955, 1.584, 1.043, 1.672, 1.04 ] + }, + { + "time": 1.7333, + "x": 0.954, + "y": 1.029, + "curve": [ 1.843, 0.954, 1.933, 1, 1.825, 1.013, 1.933, 1 ] + }, + { "time": 2 } + ] + }, + "rear-upper-arm": { + "rotate": [ + { + "time": 0.9, + "value": 39.24, + "curve": [ 0.968, 39.93, 1.267, 85.31 ] + }, + { + "time": 1.4667, + "value": 112.27, + "curve": [ 1.555, 124.24, 1.576, 126.44 ] + }, + { + "time": 1.6333, + "value": 126.44, + "curve": [ 1.782, 126.44, 1.992, 94.55 ] + }, + { + "time": 2.1, + "value": 79.96, + "curve": [ 2.216, 64.26, 2.407, 34.36 ] + }, + { + "time": 2.5667, + "value": 33.38, + "curve": [ 2.815, 31.87, 3.1, 39.2 ] + }, + { "time": 3.1667, "value": 39.2 } + ] + }, + "back-foot-tip": { + "rotate": [ + { + "time": 1.0667, + "value": 56.07, + "curve": [ 1.138, 59.21, 1.192, 59.65 ] + }, + { + "time": 1.2333, + "value": 59.46, + "curve": [ 1.295, 59.17, 1.45, 22.54 ] + }, + { "time": 1.4667, "value": -0.84 } + ] + }, + "front-upper-arm": { + "rotate": [ + { + "time": 1.0667, + "value": 118.03, + "curve": [ 1.075, 93.64, 1.358, -34.03 ] + }, + { + "time": 1.6667, + "value": -33.94, + "curve": [ 1.808, -33.89, 1.879, -25 ] + }, + { + "time": 1.9667, + "value": -25.19, + "curve": [ 2.09, -25.46, 2.312, -34.58 ] + }, + { + "time": 2.3667, + "value": -38.36, + "curve": [ 2.465, -45.18, 2.557, -60.1 ] + }, + { + "time": 2.8333, + "value": -61.1, + "curve": [ 2.843, -61.06, 3.16, -60.87 ] + }, + { "time": 3.1667, "value": -60.87 } + ] + }, + "front-bracer": { + "rotate": [ + { + "time": 1.0667, + "value": 0.66, + "curve": [ 1.108, 0.66, 1.221, 44.95 ] + }, + { + "time": 1.2333, + "value": 49.25, + "curve": [ 1.263, 59.42, 1.342, 68.06 ] + }, + { + "time": 1.3667, + "value": 68.34, + "curve": [ 1.409, 68.8, 1.476, 4.9 ] + }, + { + "time": 1.5, + "value": -2.05, + "curve": [ 1.529, -10.3, 1.695, -15.95 ] + }, + { + "time": 1.7333, + "value": -17.38, + "curve": [ 1.807, -20.1, 1.878, -21.19 ] + }, + { + "time": 1.9333, + "value": -21.08, + "curve": [ 2.073, -20.8, 2.146, -7.63 ] + }, + { + "time": 2.1667, + "value": -3.64, + "curve": [ 2.186, 0.12, 2.275, 15.28 ] + }, + { + "time": 2.3333, + "value": 21.78, + "curve": [ 2.392, 28.31, 2.575, 37.66 ] + }, + { + "time": 2.7, + "value": 39.43, + "curve": [ 2.947, 42.93, 3.02, 42.46 ] + }, + { "time": 3.1667, "value": 42.46 } + ] + }, + "front-thigh": { + "translate": [ + { "time": 1.1, "x": -6.41, "y": 18.23, "curve": "stepped" }, + { "time": 1.1333, "x": -6.41, "y": 18.23 }, + { "time": 1.2, "x": 1.61, "y": 3.66 }, + { "time": 1.2333, "x": 4.5, "y": -3.15 }, + { "time": 1.3667, "x": -3.79, "y": 2.94 }, + { "time": 1.4, "x": -8.37, "y": 8.72 }, + { "time": 1.4333, "x": -11.26, "y": 16.99 }, + { "time": 1.4667, "x": -9.89, "y": 24.73, "curve": "stepped" }, + { "time": 1.8667, "x": -9.89, "y": 24.73 }, + { "time": 2.1 } + ] + }, + "front-foot-tip": { + "rotate": [ + { "time": 1.0667, "value": 42.55, "curve": "stepped" }, + { "time": 1.1333, "value": 42.55 }, + { "time": 1.2333, "value": 17.71 }, + { "time": 1.3667, "value": 3.63 }, + { "time": 1.4333 } + ] + }, + "rear-bracer": { + "rotate": [ + { + "time": 1.0667, + "value": 108.71, + "curve": [ 1.082, 108.29, 1.437, 50.73 ] + }, + { + "time": 1.5667, + "value": 24.87, + "curve": [ 1.62, 14.2, 1.66, -11.74 ] + }, + { + "time": 1.7333, + "value": -11.74, + "curve": [ 1.961, -11.73, 2.172, 1.66 ] + }, + { + "time": 2.2667, + "value": 7.88, + "curve": [ 2.331, 12.13, 2.439, 18.65 ] + }, + { + "time": 2.5333, + "value": 18.72, + "curve": [ 2.788, 18.91, 3.145, -0.3 ] + }, + { "time": 3.1667 } + ] + }, + "front-fist": { + "rotate": [ + { + "time": 1.1, + "value": 6.32, + "curve": [ 1.11, 3.31, 1.153, -5.07 ] + }, + { + "time": 1.2333, + "value": -5.13, + "curve": [ 1.311, -5.19, 1.364, 34.65 ] + }, + { + "time": 1.4667, + "value": 34.53, + "curve": [ 1.574, 34.41, 1.547, -55.78 ] + }, + { + "time": 1.8667, + "value": -54.7, + "curve": [ 1.947, -54.7, 2.03, -53.94 ] + }, + { + "time": 2.1333, + "value": -42.44, + "curve": [ 2.215, -33.42, 2.358, -4.43 ] + }, + { + "time": 2.4, + "value": 0.03, + "curve": [ 2.444, 4.66, 2.536, 8.2 ] + }, + { + "time": 2.6333, + "value": 8.2, + "curve": [ 2.733, 8.19, 2.804, -0.67 ] + }, + { + "time": 2.9, + "value": -0.82, + "curve": [ 3.127, -1.16, 3.093, 0 ] + }, + { "time": 3.1667 } + ] + }, + "gun": { + "rotate": [ + { + "time": 1.2667, + "curve": [ 1.35, 0, 1.549, 7.49 ] + }, + { + "time": 1.6, + "value": 9.5, + "curve": [ 1.663, 12.02, 1.846, 19.58 ] + }, + { + "time": 1.9333, + "value": 19.43, + "curve": [ 1.985, 19.4, 2.057, 2.98 ] + }, + { + "time": 2.2, + "value": 2.95, + "curve": [ 2.304, 3.55, 2.458, 10.8 ] + }, + { + "time": 2.5, + "value": 10.8, + "curve": [ 2.642, 10.8, 2.873, -2.54 ] + }, + { + "time": 2.9333, + "value": -2.55, + "curve": [ 3.09, -2.57, 3.08, 0 ] + }, + { "time": 3.1667 } + ] + }, + "hair2": { + "rotate": [ + { + "time": 1.0667, + "value": 26.19, + "curve": [ 1.158, 26.19, 1.368, 26 ] + }, + { + "time": 1.4333, + "value": 24.43, + "curve": [ 1.534, 22.03, 2, -29.14 ] + }, + { + "time": 2.2, + "value": -29.14, + "curve": [ 2.292, -29.14, 2.475, 6.71 ] + }, + { + "time": 2.5667, + "value": 6.71, + "curve": [ 2.675, 6.71, 2.814, -5.06 ] + }, + { + "time": 2.9, + "value": -5.06, + "curve": [ 2.973, -5.06, 3.123, 0 ] + }, + { "time": 3.1667 } + ] + }, + "hair4": { + "rotate": [ + { + "time": 1.0667, + "value": 5.21, + "curve": [ 1.108, 5.21, 1.192, 26.19 ] + }, + { + "time": 1.2333, + "value": 26.19, + "curve": [ 1.317, 26.19, 1.483, 10.63 ] + }, + { + "time": 1.5667, + "value": 10.63, + "curve": [ 1.627, 10.63, 1.642, 17.91 ] + }, + { + "time": 1.7, + "value": 17.94, + "curve": [ 1.761, 17.97, 1.774, 8.22 ] + }, + { + "time": 1.8, + "value": 3.33, + "curve": [ 1.839, -4.21, 1.95, -22.67 ] + }, + { + "time": 2, + "value": -22.67, + "curve": [ 2.025, -22.67, 2.123, -21.86 ] + }, + { + "time": 2.1667, + "value": -18.71, + "curve": [ 2.228, -14.31, 2.294, -0.3 ] + }, + { + "time": 2.3667, + "value": 6.36, + "curve": [ 2.433, 12.45, 2.494, 19.21 ] + }, + { + "time": 2.6, + "value": 19.21, + "curve": [ 2.729, 19.21, 2.854, 6.75 ] + }, + { + "time": 2.9333, + "value": 4.62, + "curve": [ 3.09, 0.45, 3.062, 0 ] + }, + { "time": 3.1667 } + ] + }, + "hair3": { + "rotate": [ + { + "time": 1.4333, + "curve": [ 1.45, 0, 1.452, 11.29 ] + }, + { + "time": 1.5, + "value": 11.21, + "curve": [ 1.596, 11.06, 1.573, -14.17 ] + }, + { + "time": 1.7333, + "value": -20.4, + "curve": [ 1.851, -24.98, 1.943, -28.45 ] + }, + { + "time": 2.2, + "value": -28.75, + "curve": [ 2.317, -28.75, 2.55, 7.04 ] + }, + { + "time": 2.6667, + "value": 7.04, + "curve": [ 2.792, 7.04, 2.885, -5.19 ] + }, + { + "time": 2.9667, + "value": -5.19, + "curve": [ 3.037, -5.19, 3.096, 0 ] + }, + { "time": 3.1667 } + ] + }, + "hair1": { + "rotate": [ + { + "time": 1.2333, + "curve": [ 1.283, 0, 1.349, 3.99 ] + }, + { + "time": 1.4333, + "value": 6.58, + "curve": [ 1.497, 8.54, 1.683, 9.35 ] + }, + { + "time": 1.7667, + "value": 9.35, + "curve": [ 1.825, 9.35, 1.945, -8.71 ] + }, + { + "time": 2, + "value": -11.15, + "curve": [ 2.058, -13.71, 2.2, -14.97 ] + }, + { + "time": 2.2667, + "value": -14.97, + "curve": [ 2.367, -14.97, 2.567, 18.77 ] + }, + { + "time": 2.6667, + "value": 18.77, + "curve": [ 2.733, 18.77, 2.817, 8.29 ] + }, + { + "time": 2.8667, + "value": 6.51, + "curve": [ 2.988, 2.17, 3.058, 0 ] + }, + { "time": 3.1667 } + ] + }, + "flare1": { + "rotate": [ + { "time": 1.1, "value": 8.2 } + ], + "translate": [ + { "time": 1.1, "x": -19.97, "y": 149.68 }, + { "time": 1.2, "x": 3.85, "y": 152.43 }, + { "time": 1.2333, "x": -15.42, "y": 152.29 } + ], + "scale": [ + { + "time": 1.1, + "x": 0.805, + "y": 0.805, + "curve": [ 1.119, 0.763, 1.16, 1.162, 1.117, 0.805, 1.15, 0.605 ] + }, + { + "time": 1.1667, + "x": 1.279, + "y": 0.605, + "curve": [ 1.177, 1.47, 1.192, 2.151, 1.175, 0.605, 1.192, 0.911 ] + }, + { + "time": 1.2, + "x": 2.151, + "y": 0.911, + "curve": [ 1.208, 2.151, 1.231, 1.668, 1.208, 0.911, 1.227, 0.844 ] + }, + { + "time": 1.2333, + "x": 1.608, + "y": 0.805, + "curve": [ 1.249, 1.205, 1.283, 0.547, 1.254, 0.685, 1.283, 0.416 ] + }, + { "time": 1.3, "x": 0.547, "y": 0.416 } + ], + "shear": [ + { "time": 1.1, "y": 4.63 }, + { "time": 1.2333, "x": -5.74, "y": 4.63 } + ] + }, + "flare2": { + "rotate": [ + { "time": 1.1, "value": 12.29 } + ], + "translate": [ + { "time": 1.1, "x": -8.63, "y": 132.96 }, + { "time": 1.2, "x": 4.35, "y": 132.93 } + ], + "scale": [ + { "time": 1.1, "x": 0.864, "y": 0.864 }, + { "time": 1.1667, "x": 0.945, "y": 0.945 }, + { "time": 1.2, "x": 1.511, "y": 1.081 } + ], + "shear": [ + { "time": 1.1, "y": 24.03 } + ] + }, + "flare3": { + "rotate": [ + { "time": 1.1667, "value": 2.88 } + ], + "translate": [ + { "time": 1.1667, "x": 3.24, "y": 114.81 } + ], + "scale": [ + { "time": 1.1667, "x": 0.668, "y": 0.668 } + ], + "shear": [ + { "time": 1.1667, "y": 38.59 } + ] + }, + "flare4": { + "rotate": [ + { "time": 1.1667, "value": -8.64 } + ], + "translate": [ + { "time": 1.1667, "x": -3.82, "y": 194.06 }, + { "time": 1.2667, "x": -1.82, "y": 198.47, "curve": "stepped" }, + { "time": 1.3, "x": -1.94, "y": 187.81 } + ], + "scale": [ + { "time": 1.1667, "x": 0.545, "y": 0.545 }, + { "time": 1.2667, "x": 0.757, "y": 0.757 } + ], + "shear": [ + { "time": 1.1667, "x": 7.42, "y": -22.04 } + ] + }, + "flare5": { + "translate": [ + { "time": 1.2, "x": -11.17, "y": 176.42 }, + { "time": 1.2333, "x": -8.56, "y": 179.04, "curve": "stepped" }, + { "time": 1.3, "x": -14.57, "y": 168.69 } + ], + "scale": [ + { "time": 1.2333, "x": 1.146 }, + { "time": 1.3, "x": 0.703, "y": 0.61 } + ], + "shear": [ + { "time": 1.2, "x": 6.9 } + ] + }, + "flare6": { + "rotate": [ + { "time": 1.2333, "value": -5.36 }, + { "time": 1.2667, "value": -0.54 } + ], + "translate": [ + { "time": 1.2333, "x": 14.52, "y": 204.67 }, + { "time": 1.2667, "x": 19.16, "y": 212.9, "curve": "stepped" }, + { "time": 1.3, "x": 9.23, "y": 202.85 } + ], + "scale": [ + { "time": 1.2333, "x": 0.777, "y": 0.49 }, + { "time": 1.2667, "x": 0.777, "y": 0.657 }, + { "time": 1.3, "x": 0.475, "y": 0.401 } + ] + }, + "flare7": { + "rotate": [ + { "time": 1.1, "value": 5.98 }, + { "time": 1.1333, "value": 32.82 } + ], + "translate": [ + { "time": 1.1, "x": -6.34, "y": 112.98 }, + { "time": 1.1333, "x": 2.66, "y": 111.6 } + ], + "scale": [ + { "time": 1.1, "x": 0.588, "y": 0.588 } + ], + "shear": [ + { "time": 1.1333, "x": -19.93 } + ] + }, + "flare8": { + "rotate": [ + { "time": 1.2333, "value": -6.85 } + ], + "translate": [ + { "time": 1.1667, "x": 66.67, "y": 125.52, "curve": "stepped" }, + { "time": 1.2, "x": 58.24, "y": 113.53, "curve": "stepped" }, + { "time": 1.2333, "x": 40.15, "y": 114.69 } + ], + "scale": [ + { "time": 1.1667, "x": 1.313, "y": 1.203 }, + { "time": 1.2333, "x": 1.038, "y": 0.95 } + ], + "shear": [ + { "time": 1.2, "y": -13.01 } + ] + }, + "flare9": { + "rotate": [ + { "time": 1.1667, "value": 2.9 } + ], + "translate": [ + { "time": 1.1667, "x": 28.45, "y": 151.35, "curve": "stepped" }, + { "time": 1.2, "x": 48.8, "y": 191.09, "curve": "stepped" }, + { "time": 1.2333, "x": 52, "y": 182.52, "curve": "stepped" }, + { "time": 1.2667, "x": 77.01, "y": 195.96 } + ], + "scale": [ + { "time": 1.1667, "x": 0.871, "y": 1.073 }, + { "time": 1.2, "x": 0.927, "y": 0.944 }, + { "time": 1.2333, "x": 1.165, "y": 1.336 } + ], + "shear": [ + { "time": 1.1667, "x": 7.95, "y": 25.48 } + ] + }, + "flare10": { + "rotate": [ + { "time": 1.1667, "value": 2.18 } + ], + "translate": [ + { "time": 1.1667, "x": 55.64, "y": 137.64, "curve": "stepped" }, + { "time": 1.2, "x": 90.49, "y": 151.07, "curve": "stepped" }, + { "time": 1.2333, "x": 114.06, "y": 153.05, "curve": "stepped" }, + { "time": 1.2667, "x": 90.44, "y": 164.61 } + ], + "scale": [ + { "time": 1.1667, "x": 2.657, "y": 0.891 }, + { "time": 1.2, "x": 3.314, "y": 1.425 }, + { "time": 1.2333, "x": 2.871, "y": 0.924 }, + { "time": 1.2667, "x": 2.317, "y": 0.775 } + ], + "shear": [ + { "time": 1.1667, "x": -1.35 } + ] + }, + "torso2": { + "rotate": [ + { + "time": 1, + "curve": [ 1.117, 0, 1.255, 24.94 ] + }, + { + "time": 1.4, + "value": 24.94, + "curve": [ 1.477, 24.94, 1.59, -17.62 ] + }, + { + "time": 1.6333, + "value": -19.48, + "curve": [ 1.717, -23.1, 1.784, -26.12 ] + }, + { + "time": 1.9333, + "value": -26.14, + "curve": [ 2.067, -26.15, 2.158, 4.3 ] + }, + { + "time": 2.3, + "value": 4.22, + "curve": [ 2.45, 4.13, 2.579, -1.76 ] + }, + { + "time": 2.7333, + "value": -1.8, + "curve": [ 2.816, -1.82, 2.857, -2.94 ] + }, + { + "time": 2.9333, + "value": -2.99, + "curve": [ 3.056, -3.08, 3.09, 0 ] + }, + { "time": 3.1667 } + ] + }, + "torso3": { + "rotate": [ + { + "time": 1.3, + "curve": [ 1.352, 0, 1.408, 6.47 ] + }, + { + "time": 1.4667, + "value": 6.43, + "curve": [ 1.55, 6.39, 1.723, -5.05 ] + }, + { + "time": 1.7333, + "value": -5.53, + "curve": [ 1.782, -7.72, 1.843, -16.94 ] + }, + { + "time": 1.9667, + "value": -16.86, + "curve": [ 2.111, -16.78, 2.259, -3.97 ] + }, + { + "time": 2.4, + "value": -2.43, + "curve": [ 2.525, -1.12, 2.639, -0.5 ] + }, + { + "time": 2.7333, + "value": -0.49, + "curve": [ 2.931, -0.47, 2.999, -2.15 ] + }, + { "time": 3.1667, "value": -2.15 } + ] + }, + "head-control": { + "translate": [ + { + "time": 1.2333, + "curve": [ 1.25, 0, 1.474, 6.89, 1.25, 0, 1.496, 0.98 ] + }, + { + "time": 1.6667, + "x": 11.99, + "y": -6.42, + "curve": [ 1.743, 14.01, 1.86, 14.33, 1.785, -11.55, 1.86, -27.1 ] + }, + { + "time": 1.9667, + "x": 13.91, + "y": -26.88, + "curve": [ 2.074, 13.49, 2.244, 8.13, 2.074, -26.65, 2.215, -21.78 ] + }, + { + "time": 2.3, + "x": 6.07, + "y": -16.64, + "curve": [ 2.416, 1.84, 2.497, -1.41, 2.417, -9.57, 2.526, -1.72 ] + }, + { + "time": 2.5667, + "x": -3.78, + "y": -1.71, + "curve": [ 2.661, -6.98, 2.76, -8.76, 2.692, -1.68, 2.821, -15.75 ] + }, + { + "time": 2.9, + "x": -8.32, + "y": -16.7, + "curve": [ 2.962, -8.12, 3.082, -0.04, 2.958, -17.39, 3.089, 0 ] + }, + { "time": 3.1667 } + ] + }, + "front-shoulder": { + "translate": [ + { + "time": 1.3333, + "curve": [ 1.488, 0, 1.717, 0.21, 1.488, 0, 1.688, -30.29 ] + }, + { + "time": 1.9, + "x": 0.83, + "y": -30.29, + "curve": [ 2.078, 1.43, 2.274, 2.88, 2.071, -30.29, 2.289, 4.48 ] + }, + { + "time": 2.4333, + "x": 2.89, + "y": 4.59, + "curve": [ 2.604, 2.89, 2.677, -0.68, 2.57, 4.7, 2.694, -2.43 ] + }, + { + "time": 2.7667, + "x": -0.67, + "y": -2.47, + "curve": [ 2.866, -0.67, 2.986, -0.07, 2.882, -2.47, 3.036, -0.06 ] + }, + { "time": 3.1667 } + ] + } + }, + "ik": { + "rear-leg-ik": [ + { "time": 3.1667, "softness": 10, "bendPositive": false } + ] + } + }, + "run": { + "slots": { + "mouth": { + "attachment": [ + { "name": "mouth-grind" } + ] + } + }, + "bones": { + "front-thigh": { + "translate": [ + { + "x": -5.14, + "y": 11.13, + "curve": [ 0.033, -7.77, 0.112, -9.03, 0.034, 11.13, 0.108, 9.74 ] + }, + { + "time": 0.1667, + "x": -9.03, + "y": 7.99, + "curve": [ 0.23, -9.05, 0.314, -1.34, 0.236, 5.93, 0.28, 3.22 ] + }, + { + "time": 0.3333, + "x": 0.41, + "y": 3.19, + "curve": [ 0.352, 2.09, 0.449, 11.16, 0.384, 3.16, 0.449, 4.98 ] + }, + { + "time": 0.5, + "x": 11.17, + "y": 6.76, + "curve": [ 0.571, 10.79, 0.621, -1.83, 0.542, 8.21, 0.625, 11.13 ] + }, + { "time": 0.6667, "x": -5.14, "y": 11.13 } + ] + }, + "torso": { + "rotate": [ + { + "value": -37.66, + "curve": [ 0.034, -37.14, 0.107, -36.21 ] + }, + { + "time": 0.1333, + "value": -36.21, + "curve": [ 0.158, -36.21, 0.209, -38.8 ] + }, + { + "time": 0.2333, + "value": -38.79, + "curve": [ 0.259, -38.78, 0.313, -38.03 ] + }, + { + "time": 0.3333, + "value": -37.66, + "curve": [ 0.357, -37.21, 0.4, -36.21 ] + }, + { + "time": 0.4333, + "value": -36.21, + "curve": [ 0.458, -36.21, 0.539, -38.8 ] + }, + { + "time": 0.5667, + "value": -38.8, + "curve": [ 0.592, -38.8, 0.645, -38 ] + }, + { "time": 0.6667, "value": -37.66 } + ] + }, + "rear-thigh": { + "translate": [ + { + "x": -16.41, + "y": 1.55, + "curve": [ 0.013, -15.67, 0.183, -8.55, 0.03, 2.39, 0.183, 6.17 ] + }, + { + "time": 0.2333, + "x": -8.55, + "y": 6.17, + "curve": [ 0.308, -8.55, 0.492, -19.75, 0.308, 6.17, 0.492, 0.61 ] + }, + { + "time": 0.5667, + "x": -19.75, + "y": 0.61, + "curve": [ 0.592, -19.75, 0.641, -18.06, 0.592, 0.61, 0.632, 0.78 ] + }, + { "time": 0.6667, "x": -16.41, "y": 1.55 } + ] + }, + "front-upper-arm": { + "rotate": [ + { + "value": -39.03, + "curve": [ 0.051, -0.1, 0.145, 88.36 ] + }, + { + "time": 0.2333, + "value": 88.36, + "curve": [ 0.28, 88.76, 0.324, 59.52 ] + }, + { + "time": 0.3333, + "value": 51.13, + "curve": [ 0.358, 30.2, 0.445, -74.91 ] + }, + { + "time": 0.5667, + "value": -75.82, + "curve": [ 0.599, -76.06, 0.642, -55.72 ] + }, + { "time": 0.6667, "value": -39.03 } + ] + }, + "front-bracer": { + "rotate": [ + { + "value": 20.54, + "curve": [ 0.052, 11.42, 0.089, 0.13 ] + }, + { + "time": 0.1333, + "value": 0.15, + "curve": [ 0.186, 0.17, 0.221, 26.29 ] + }, + { + "time": 0.2333, + "value": 32.37, + "curve": [ 0.247, 39.19, 0.286, 61.45 ] + }, + { + "time": 0.3333, + "value": 61.58, + "curve": [ 0.371, 61.69, 0.42, 55.79 ] + }, + { "time": 0.4667, "value": 49.68 }, + { "time": 0.6667, "value": 20.54 } + ] + }, + "front-fist": { + "rotate": [ + { + "value": -36.16, + "curve": [ 0.014, -38.8, 0.036, -43.27 ] + }, + { + "time": 0.0667, + "value": -43.37, + "curve": [ 0.102, -43.49, 0.182, -28.46 ] + }, + { + "time": 0.2, + "value": -23.04, + "curve": [ 0.23, -13.87, 0.264, 3.86 ] + }, + { + "time": 0.3333, + "value": 3.7, + "curve": [ 0.38, 3.64, 0.535, -16.22 ] + }, + { "time": 0.5667, "value": -21.29 }, + { "time": 0.6667, "value": -36.16 } + ] + }, + "rear-upper-arm": { + "rotate": [ + { + "value": 40.5, + "curve": [ 0.028, 23.74, 0.128, -79.86 ] + }, + { + "time": 0.2333, + "value": -79.87, + "curve": [ 0.38, -79.88, 0.403, 63.25 ] + }, + { + "time": 0.5667, + "value": 64.13, + "curve": [ 0.607, 64.35, 0.644, 53.1 ] + }, + { "time": 0.6667, "value": 40.5 } + ], + "translate": [ + { + "x": -3.79, + "y": -0.77, + "curve": [ 0.044, -4.58, 0.169, -5.48, 0.044, 0.93, 0.169, 2.85 ] + }, + { + "time": 0.2333, + "x": -5.48, + "y": 2.85, + "curve": [ 0.346, -5.48, 0.475, -2.68, 0.346, 2.85, 0.475, -3.13 ] + }, + { + "time": 0.5667, + "x": -2.68, + "y": -3.13, + "curve": [ 0.611, -2.68, 0.642, -3.34, 0.611, -3.13, 0.642, -1.73 ] + }, + { "time": 0.6667, "x": -3.79, "y": -0.77 } + ] + }, + "rear-bracer": { + "rotate": [ + { "value": 28.28 }, + { + "time": 0.2333, + "value": -11.12, + "curve": [ 0.252, -14.12, 0.297, -19.37 ] + }, + { + "time": 0.3333, + "value": -19.38, + "curve": [ 0.435, -19.41, 0.522, 38.96 ] + }, + { + "time": 0.5667, + "value": 38.87, + "curve": [ 0.619, 38.76, 0.644, 32.01 ] + }, + { "time": 0.6667, "value": 28.28 } + ] + }, + "neck": { + "rotate": [ + { + "value": 11.88, + "curve": [ 0.024, 11.4, 0.075, 9.74 ] + }, + { + "time": 0.1, + "value": 9.74, + "curve": [ 0.125, 9.74, 0.208, 13.36 ] + }, + { + "time": 0.2333, + "value": 13.36, + "curve": [ 0.258, 13.36, 0.321, 12.2 ] + }, + { + "time": 0.3333, + "value": 11.88, + "curve": [ 0.365, 11.06, 0.408, 9.72 ] + }, + { + "time": 0.4333, + "value": 9.72, + "curve": [ 0.458, 9.72, 0.542, 13.36 ] + }, + { + "time": 0.5667, + "value": 13.36, + "curve": [ 0.592, 13.36, 0.636, 12.48 ] + }, + { "time": 0.6667, "value": 11.88 } + ] + }, + "head": { + "rotate": [ + { + "value": 13.14, + "curve": [ 0.02, 11.99, 0.039, 8.94 ] + }, + { + "time": 0.0667, + "value": 8.93, + "curve": [ 0.122, 8.9, 0.232, 15.8 ] + }, + { + "time": 0.2667, + "value": 15.81, + "curve": [ 0.325, 15.82, 0.357, 8.95 ] + }, + { + "time": 0.4, + "value": 8.93, + "curve": [ 0.444, 8.91, 0.568, 15.8 ] + }, + { + "time": 0.6, + "value": 15.77, + "curve": [ 0.632, 15.74, 0.649, 14.05 ] + }, + { "time": 0.6667, "value": 13.14 } + ], + "scale": [ + { + "curve": [ 0.014, 0.996, 0.068, 0.991, 0.027, 1.005, 0.083, 1.012 ] + }, + { + "time": 0.1, + "x": 0.991, + "y": 1.012, + "curve": [ 0.128, 0.991, 0.205, 1.018, 0.128, 1.012, 0.197, 0.988 ] + }, + { + "time": 0.2333, + "x": 1.018, + "y": 0.988, + "curve": [ 0.272, 1.018, 0.305, 1.008, 0.262, 0.988, 0.311, 0.995 ] + }, + { + "time": 0.3333, + "curve": [ 0.351, 0.995, 0.417, 0.987, 0.359, 1.006, 0.417, 1.013 ] + }, + { + "time": 0.4333, + "x": 0.987, + "y": 1.013, + "curve": [ 0.467, 0.987, 0.533, 1.02, 0.467, 1.013, 0.533, 0.989 ] + }, + { + "time": 0.5667, + "x": 1.02, + "y": 0.989, + "curve": [ 0.592, 1.02, 0.652, 1.004, 0.592, 0.989, 0.644, 0.996 ] + }, + { "time": 0.6667 } + ] + }, + "gun": { + "rotate": [ + { + "value": 12.36, + "curve": [ 0.022, 16.28, 0.087, 20.25 ] + }, + { + "time": 0.1333, + "value": 20.19, + "curve": [ 0.168, 20.32, 0.254, -8.82 ] + }, + { + "time": 0.2667, + "value": -11.88, + "curve": [ 0.291, -17.91, 0.344, -24.11 ] + }, + { + "time": 0.4, + "value": -23.88, + "curve": [ 0.448, -23.69, 0.533, -15.47 ] + }, + { "time": 0.5667, "value": -8.69 }, + { "time": 0.6667, "value": 12.36 } + ] + }, + "hip": { + "rotate": [ + { "value": -8.24 } + ], + "translate": [ + { + "x": -3.6, + "y": -34.1, + "curve": [ 0.042, -3.84, 0.118, 7.62, 0.042, -33.74, 0.112, 20.55 ] + }, + { + "time": 0.1667, + "x": 7.61, + "y": 20.36, + "curve": [ 0.194, 7.6, 0.21, 5.06, 0.204, 20.65, 0.217, -8.69 ] + }, + { + "time": 0.2333, + "x": 1.68, + "y": -18.48, + "curve": [ 0.279, -4.99, 0.297, -5.64, 0.254, -31.08, 0.292, -34.55 ] + }, + { + "time": 0.3333, + "x": -5.76, + "y": -35, + "curve": [ 0.379, -5.9, 0.451, 6.8, 0.384, -35.56, 0.428, 17.6 ] + }, + { + "time": 0.5, + "x": 6.61, + "y": 17.01, + "curve": [ 0.536, 6.47, 0.545, 3.56, 0.533, 16.75, 0.548, -8.71 ] + }, + { + "time": 0.5667, + "x": 0.35, + "y": -18.81, + "curve": [ 0.597, -4.07, 0.642, -3.45, 0.584, -28.58, 0.642, -34.32 ] + }, + { "time": 0.6667, "x": -3.6, "y": -34.1 } + ] + }, + "front-foot-target": { + "rotate": [ + { + "value": -62.54, + "curve": [ 0.015, -74.19, 0.056, -103.19 ] + }, + { + "time": 0.0667, + "value": -111.08, + "curve": [ 0.092, -129.44, 0.189, -146.55 ] + }, + { + "time": 0.2333, + "value": -146.32, + "curve": [ 0.285, -146.06, 0.32, -125.1 ] + }, + { "time": 0.3333, "value": -117.24 }, + { + "time": 0.5, + "value": -35.07, + "curve": [ 0.522, -28.64, 0.546, -24.84 ] + }, + { + "time": 0.5667, + "value": -24.9, + "curve": [ 0.595, -25, 0.623, -40.82 ] + }, + { "time": 0.6667, "value": -62.54 } + ], + "translate": [ + { "x": 16.34, "y": 0.18 }, + { + "time": 0.0667, + "x": -101.43, + "y": 8.04, + "curve": [ 0.085, -131.35, 0.129, -207.69, 0.08, 14.9, 0.124, 113.28 ] + }, + { + "time": 0.1667, + "x": -207.92, + "y": 145.81, + "curve": [ 0.196, -208.13, 0.21, -202.91, 0.186, 160.26, 0.206, 163.48 ] + }, + { + "time": 0.2333, + "x": -189.94, + "y": 163.85, + "curve": [ 0.27, -169.94, 0.31, -126.19, 0.269, 164.35, 0.316, 85.97 ] + }, + { + "time": 0.3333, + "x": -90.56, + "y": 78.57, + "curve": [ 0.355, -57.99, 0.376, -29.14, 0.35, 71.55, 0.376, 66.4 ] + }, + { + "time": 0.4, + "x": 2.87, + "y": 66.38, + "curve": [ 0.412, 19.24, 0.469, 90.73, 0.429, 66.37, 0.469, 70.66 ] + }, + { + "time": 0.5, + "x": 117.18, + "y": 70.46, + "curve": [ 0.522, 136.24, 0.542, 151.33, 0.539, 70.2, 0.555, 38.25 ] + }, + { + "time": 0.5667, + "x": 151.49, + "y": 25.29, + "curve": [ 0.578, 146.76, 0.586, 133.13, 0.572, 19.7, 0.582, 12.23 ] + }, + { "time": 0.6, "x": 115.02, "y": 0.1 }, + { "time": 0.6667, "x": 16.34, "y": 0.18 } + ] + }, + "front-leg-target": { + "translate": [ + { "x": -13.95, "y": -30.34 } + ] + }, + "rear-foot-target": { + "rotate": [ + { "value": 18.55 }, + { + "time": 0.2333, + "value": 167.84, + "curve": [ 0.246, 153.66, 0.256, 129.74 ] + }, + { + "time": 0.2667, + "value": 124.32, + "curve": [ 0.296, 124.43, 0.313, 129.93 ] + }, + { + "time": 0.3667, + "value": 129.87, + "curve": [ 0.421, 128.32, 0.519, 0.98 ] + }, + { + "time": 0.5667, + "curve": [ 0.6, 0.27, 0.642, 4.73 ] + }, + { "time": 0.6667, "value": 18.55 } + ], + "translate": [ + { + "x": -176.39, + "y": 134.12, + "curve": [ 0.018, -142.26, 0.054, -94.41, 0.01, 120.96, 0.044, 84.08 ] + }, + { + "time": 0.0667, + "x": -73.56, + "y": 76.68, + "curve": [ 0.086, -42.82, 0.194, 101.2, 0.098, 66.73, 0.198, 60.88 ] + }, + { "time": 0.2333, "x": 98.32, "y": 32.17 }, + { "time": 0.2667, "x": 49.13, "y": -0.63 }, + { + "time": 0.4, + "x": -147.9, + "y": 0.32, + "curve": [ 0.414, -168.78, 0.478, -284.76, 0.43, 30.09, 0.478, 129.14 ] + }, + { + "time": 0.5, + "x": -283.37, + "y": 167.12, + "curve": [ 0.526, -285.66, 0.548, -280.54, 0.516, 194.84, 0.55, 216.53 ] + }, + { + "time": 0.5667, + "x": -266.98, + "y": 216.12, + "curve": [ 0.581, -256.27, 0.643, -206.54, 0.61, 214.82, 0.65, 145.33 ] + }, + { "time": 0.6667, "x": -176.39, "y": 134.12 } + ] + }, + "rear-leg-target": { + "translate": [ + { "x": 85, "y": -33.59 } + ] + }, + "back-foot-tip": { + "rotate": [ + { + "value": -147.04, + "curve": [ 0.033, -113.4, 0.161, 44.34 ] + }, + { + "time": 0.2333, + "value": 43.48, + "curve": [ 0.24, 43.41, 0.282, 35.72 ] + }, + { + "time": 0.3, + "value": 0.29, + "curve": [ 0.347, 0.28, 0.396, 4.27 ] + }, + { + "time": 0.4, + "curve": [ 0.424, -23.8, 0.525, -181.39 ] + }, + { + "time": 0.5667, + "value": -181.39, + "curve": [ 0.592, -181.39, 0.642, -169.09 ] + }, + { "time": 0.6667, "value": -147.04 } + ] + }, + "front-foot-tip": { + "rotate": [ + { + "value": -0.25, + "curve": [ 0.008, -0.25, 0.056, 1.73 ] + }, + { + "time": 0.0667, + "value": -7.68, + "curve": [ 0.075, -43.13, 0.15, -130.44 ] + }, + { + "time": 0.2, + "value": -130.08, + "curve": [ 0.239, -129.79, 0.272, -126.8 ] + }, + { + "time": 0.3, + "value": -116.24, + "curve": [ 0.333, -103.91, 0.348, -86.1 ] + }, + { + "time": 0.3667, + "value": -71.08, + "curve": [ 0.386, -55.25, 0.415, -32.44 ] + }, + { + "time": 0.4333, + "value": -21.63, + "curve": [ 0.47, -0.01, 0.542, 33.42 ] + }, + { + "time": 0.5667, + "value": 33.2, + "curve": [ 0.622, 32.7, 0.569, 0.64 ] + }, + { "time": 0.6667, "value": -0.25 } + ] + }, + "hair1": { + "rotate": [ + { + "value": -6.81, + "curve": [ 0.087, -6.81, 0.143, -5.75 ] + }, + { + "time": 0.1667, + "value": -4.3, + "curve": [ 0.183, -3.28, 0.209, 2.79 ] + }, + { + "time": 0.2333, + "value": 2.78, + "curve": [ 0.262, 2.77, 0.305, -6.63 ] + }, + { + "time": 0.3333, + "value": -6.64, + "curve": [ 0.419, -6.68, 0.49, -4.84 ] + }, + { + "time": 0.5, + "value": -4.38, + "curve": [ 0.518, -3.56, 0.574, 2.32 ] + }, + { + "time": 0.6, + "value": 2.33, + "curve": [ 0.643, 2.35, 0.633, -6.81 ] + }, + { "time": 0.6667, "value": -6.81 } + ] + }, + "hair2": { + "rotate": [ + { + "value": -6.81, + "curve": [ 0.014, -3.17, 0.109, 43.93 ] + }, + { + "time": 0.1333, + "value": 43.95, + "curve": [ 0.177, 43.97, 0.192, -13.76 ] + }, + { + "time": 0.2667, + "value": -13.83, + "curve": [ 0.302, -13.72, 0.322, -8.86 ] + }, + { + "time": 0.3333, + "value": -6.6, + "curve": [ 0.349, -3.5, 0.436, 41.1 ] + }, + { + "time": 0.4667, + "value": 41.05, + "curve": [ 0.51, 40.99, 0.549, -14.06 ] + }, + { + "time": 0.6, + "value": -14.18, + "curve": [ 0.63, -14.26, 0.656, -9.04 ] + }, + { "time": 0.6667, "value": -6.81 } + ] + }, + "hair3": { + "rotate": [ + { + "value": -6.81, + "curve": [ 0.079, -6.83, 0.108, 0.3 ] + }, + { + "time": 0.1333, + "value": 1.96, + "curve": [ 0.177, 4.89, 0.208, 6.28 ] + }, + { + "time": 0.2333, + "value": 6.29, + "curve": [ 0.313, 6.31, 0.383, 3.49 ] + }, + { + "time": 0.4, + "value": 2.58, + "curve": [ 0.442, 0.28, 0.523, -6.81 ] + }, + { "time": 0.6, "value": -6.81 } + ] + }, + "hair4": { + "rotate": [ + { + "value": -6.81, + "curve": [ 0.011, -4.06, 0.108, 24.92 ] + }, + { + "time": 0.1333, + "value": 24.92, + "curve": [ 0.158, 24.92, 0.208, -10.62 ] + }, + { + "time": 0.2333, + "value": -10.62, + "curve": [ 0.254, -10.62, 0.312, -9.73 ] + }, + { + "time": 0.3333, + "value": -6.4, + "curve": [ 0.356, -2.95, 0.438, 24.93 ] + }, + { + "time": 0.4667, + "value": 24.93, + "curve": [ 0.492, 24.93, 0.575, -9.78 ] + }, + { + "time": 0.6, + "value": -9.78, + "curve": [ 0.617, -9.78, 0.655, -8.63 ] + }, + { "time": 0.6667, "value": -6.81 } + ] + }, + "torso2": { + "rotate": [ + { + "value": 3.5, + "curve": [ 0.07, 3.51, 0.075, 8.69 ] + }, + { + "time": 0.1, + "value": 8.69, + "curve": [ 0.139, 8.69, 0.214, 6.9 ] + }, + { + "time": 0.2333, + "value": 6.33, + "curve": [ 0.266, 5.34, 0.285, 3.48 ] + }, + { + "time": 0.3333, + "value": 3.48, + "curve": [ 0.398, 3.48, 0.408, 8.68 ] + }, + { + "time": 0.4333, + "value": 8.68, + "curve": [ 0.458, 8.68, 0.551, 6.8 ] + }, + { + "time": 0.5667, + "value": 6.26, + "curve": [ 0.598, 5.17, 0.642, 3.49 ] + }, + { "time": 0.6667, "value": 3.5 } + ] + }, + "torso3": { + "rotate": [ + { + "value": 4.52, + "curve": [ 0.067, 4.54, 0.075, -7.27 ] + }, + { + "time": 0.1, + "value": -7.27, + "curve": [ 0.125, -7.27, 0.227, 0.84 ] + }, + { + "time": 0.2333, + "value": 1.24, + "curve": [ 0.254, 2.5, 0.301, 4.51 ] + }, + { + "time": 0.3333, + "value": 4.52, + "curve": [ 0.386, 4.54, 0.408, -7.35 ] + }, + { + "time": 0.4333, + "value": -7.35, + "curve": [ 0.458, -7.35, 0.549, -0.14 ] + }, + { + "time": 0.5667, + "value": 0.95, + "curve": [ 0.586, 2.18, 0.632, 4.54 ] + }, + { "time": 0.6667, "value": 4.52 } + ] + }, + "aim-constraint-target": { + "rotate": [ + { "value": 30.57 } + ] + }, + "rear-foot": { + "rotate": [ + { "value": -6.5 } + ] + }, + "front-foot": { + "rotate": [ + { "value": 4.5 } + ] + }, + "head-control": { + "translate": [ + { + "y": -9.94, + "curve": [ 0.058, 0, 0.175, -15.32, 0.044, -4.19, 0.175, 5 ] + }, + { + "time": 0.2333, + "x": -15.32, + "y": 5, + "curve": [ 0.317, -15.32, 0.429, -9.74, 0.317, 5, 0.382, -31.71 ] + }, + { + "time": 0.4667, + "x": -7.81, + "y": -31.59, + "curve": [ 0.507, -5.76, 0.617, 0, 0.549, -31.47, 0.628, -13.33 ] + }, + { "time": 0.6667, "y": -9.94 } + ] + }, + "front-shoulder": { + "translate": [ + { + "x": -0.74, + "y": 11.22, + "curve": [ 0.061, -0.74, 0.144, 1.17, 0.061, 11.22, 0.143, -17.93 ] + }, + { + "time": 0.2333, + "x": 1.19, + "y": -17.9, + "curve": [ 0.54, 1.25, 0.558, -0.74, 0.545, -17.8, 0.558, 11.22 ] + }, + { "time": 0.6667, "x": -0.74, "y": 11.22 } + ] + }, + "back-shoulder": { + "translate": [ + { + "curve": [ 0.083, 0, 0.25, 0, 0.083, 0, 0.25, 8.93 ] + }, + { + "time": 0.3333, + "y": 8.93, + "curve": [ 0.417, 0, 0.583, 0, 0.417, 8.93, 0.583, 0 ] + }, + { "time": 0.6667 } + ] + } + }, + "ik": { + "front-leg-ik": [ + { "softness": 10, "bendPositive": false }, + { "time": 0.5667, "softness": 14.8, "bendPositive": false }, + { "time": 0.6, "softness": 48.2, "bendPositive": false }, + { "time": 0.6667, "softness": 10, "bendPositive": false } + ], + "rear-leg-ik": [ + { "bendPositive": false }, + { "time": 0.1667, "softness": 22.5, "bendPositive": false }, + { "time": 0.3, "softness": 61.4, "bendPositive": false }, + { "time": 0.6667, "bendPositive": false } + ] + }, + "events": [ + { "time": 0.2333, "name": "footstep" }, + { "time": 0.5667, "name": "footstep" } + ] + }, + "run-to-idle": { + "slots": { + "front-fist": { + "attachment": [ + { "name": "front-fist-open" } + ] + } + }, + "bones": { + "front-foot-target": { + "translate": [ + { + "x": -16.5, + "y": 3.41, + "curve": [ 0.033, -16.5, 0.1, -69.06, 0.033, 3.41, 0.1, 0 ] + }, + { "time": 0.1333, "x": -69.06 } + ] + }, + "hip": { + "translate": [ + { + "x": -28.78, + "y": -72.96, + "curve": [ 0.036, -28.63, 0.2, -10.85, 0.135, -62.35, 0.2, -23.15 ] + }, + { "time": 0.2667, "x": -11.97, "y": -23.15 } + ] + }, + "rear-foot-target": { + "translate": [ + { + "x": 33.15, + "y": 31.61, + "curve": [ 0.017, 33.15, 0.05, 24.41, 0.017, 31.61, 0.041, 20.73 ] + }, + { + "time": 0.0667, + "x": 24.41, + "y": 0.19, + "curve": [ 0.117, 24.41, 0.217, 48.87, 0.117, 0.19, 0.217, 0 ] + }, + { "time": 0.2667, "x": 48.87 } + ] + }, + "front-upper-arm": { + "rotate": [ + { + "value": -80.61, + "curve": [ 0.067, -80.61, 0.2, -60.87 ] + }, + { "time": 0.2667, "value": -60.87 } + ] + }, + "front-bracer": { + "rotate": [ + { + "value": 8.79, + "curve": [ 0.041, 8.79, 0.115, 6.3 ] + }, + { + "time": 0.1667, + "value": 6.41, + "curve": [ 0.201, 6.48, 0.241, 42.46 ] + }, + { "time": 0.2667, "value": 42.46 } + ] + }, + "rear-upper-arm": { + "rotate": [ + { + "value": 55.3, + "curve": [ 0.067, 55.3, 0.2, 39.2 ] + }, + { "time": 0.2667, "value": 39.2 } + ] + }, + "head": { + "rotate": [ + { + "curve": [ 0.05, 0, 0.083, 2.67 ] + }, + { + "time": 0.1333, + "value": 2.67, + "curve": [ 0.15, 2.67, 0.25, -6.75 ] + }, + { "time": 0.2667, "value": -6.75 } + ] + }, + "front-fist": { + "rotate": [ + { + "value": 38.26, + "curve": [ 0.041, 38.26, 0.127, -2.19 ] + }, + { + "time": 0.1667, + "value": -3, + "curve": [ 0.209, -3.84, 0.241, 0 ] + }, + { "time": 0.2667 } + ], + "scale": [ + { + "x": 0.844, + "curve": [ 0.067, 0.844, 0.2, 1, 0.067, 1, 0.2, 1 ] + }, + { "time": 0.2667 } + ] + }, + "rear-bracer": { + "rotate": [ + { + "value": 57.24, + "curve": [ 0.067, 57.24, 0.2, 0 ] + }, + { "time": 0.2667 } + ] + }, + "gun": { + "rotate": [ + { + "value": 2.28, + "curve": [ 0.041, 2.28, 0.105, 15.34 ] + }, + { + "time": 0.1667, + "value": 15.32, + "curve": [ 0.205, 15.31, 0.241, 0 ] + }, + { "time": 0.2667 } + ] + }, + "torso": { + "rotate": [ + { + "value": -12.98, + "curve": [ 0.033, -12.98, 0.103, -14.81 ] + }, + { + "time": 0.1333, + "value": -16.63, + "curve": [ 0.168, -18.69, 0.233, -22.88 ] + }, + { "time": 0.2667, "value": -22.88 } + ], + "scale": [ + { + "x": 0.963, + "y": 1.074, + "curve": [ 0.067, 0.963, 0.132, 1, 0.067, 1.074, 0.132, 1 ] + }, + { "time": 0.2667 } + ] + }, + "neck": { + "rotate": [ + {}, + { "time": 0.2667, "value": 3.78 } + ] + }, + "hair3": { + "rotate": [ + { + "curve": [ 0.033, 0, 0.1, 0.88 ] + }, + { + "time": 0.1333, + "value": 0.88, + "curve": [ 0.167, 0.88, 0.233, 0 ] + }, + { "time": 0.2667 } + ] + }, + "hair4": { + "rotate": [ + { + "curve": [ 0.033, 0, 0.1, 15.97 ] + }, + { + "time": 0.1333, + "value": 15.97, + "curve": [ 0.167, 15.97, 0.233, 0 ] + }, + { "time": 0.2667 } + ] + }, + "hair1": { + "rotate": [ + { + "curve": [ 0.033, 0, 0.1, 10.76 ] + }, + { + "time": 0.1333, + "value": 10.76, + "curve": [ 0.167, 10.76, 0.233, 0 ] + }, + { "time": 0.2667 } + ] + }, + "hair2": { + "rotate": [ + { + "curve": [ 0.014, -2.28, 0.042, -7.84 ] + }, + { + "time": 0.0667, + "value": -7.82, + "curve": [ 0.108, -7.79, 0.166, 6.57 ] + }, + { + "time": 0.2, + "value": 6.67, + "curve": [ 0.222, 6.73, 0.255, 1.98 ] + }, + { "time": 0.2667 } + ] + }, + "torso2": { + "rotate": [ + { + "curve": [ 0.041, 0, 0.107, 3.03 ] + }, + { + "time": 0.1667, + "value": 3.03, + "curve": [ 0.205, 3.03, 0.241, 0 ] + }, + { "time": 0.2667 } + ] + }, + "torso3": { + "rotate": [ + { + "curve": [ 0.049, 0, 0.166, 0.66 ] + }, + { + "time": 0.2, + "value": 0.66, + "curve": [ 0.232, 0.65, 0.249, -2.15 ] + }, + { "time": 0.2667, "value": -2.15 } + ] + }, + "head-control": { + "translate": [ + { "x": -10.12, "y": 8.71 }, + { "time": 0.2667 } + ] + }, + "front-shoulder": { + "translate": [ + { "x": 4.91, "y": 11.54 }, + { "time": 0.2667 } + ] + } + } + }, + "shoot": { + "slots": { + "muzzle": { + "rgba": [ + { "time": 0.1333, "color": "ffffffff" }, + { "time": 0.2, "color": "ffffff62" } + ], + "attachment": [ + { "time": 0.0333, "name": "muzzle01" }, + { "time": 0.0667, "name": "muzzle02" }, + { "time": 0.1, "name": "muzzle03" }, + { "time": 0.1333, "name": "muzzle04" }, + { "time": 0.1667, "name": "muzzle05" }, + { "time": 0.2 } + ] + }, + "muzzle-glow": { + "rgba": [ + { "color": "ff0c0c00" }, + { + "time": 0.0333, + "color": "ffc9adff", + "curve": [ 0.255, 1, 0.273, 1, 0.255, 0.76, 0.273, 0.4, 0.255, 0.65, 0.273, 0.22, 0.255, 1, 0.273, 1 ] + }, + { "time": 0.3, "color": "ff400cff" }, + { "time": 0.6333, "color": "ff0c0c00" } + ], + "attachment": [ + { "name": "muzzle-glow" } + ] + }, + "muzzle-ring": { + "rgba": [ + { + "time": 0.0333, + "color": "d8baffff", + "curve": [ 0.202, 0.85, 0.214, 0.84, 0.202, 0.73, 0.214, 0.73, 0.202, 1, 0.214, 1, 0.202, 1, 0.214, 0.21 ] + }, + { "time": 0.2333, "color": "d7baff00" } + ], + "attachment": [ + { "time": 0.0333, "name": "muzzle-ring" }, + { "time": 0.2333 } + ] + }, + "muzzle-ring2": { + "rgba": [ + { + "time": 0.0333, + "color": "d8baffff", + "curve": [ 0.174, 0.85, 0.184, 0.84, 0.174, 0.73, 0.184, 0.73, 0.174, 1, 0.184, 1, 0.174, 1, 0.184, 0.21 ] + }, + { "time": 0.2, "color": "d7baff00" } + ], + "attachment": [ + { "time": 0.0333, "name": "muzzle-ring" }, + { "time": 0.2 } + ] + }, + "muzzle-ring3": { + "rgba": [ + { + "time": 0.0333, + "color": "d8baffff", + "curve": [ 0.174, 0.85, 0.184, 0.84, 0.174, 0.73, 0.184, 0.73, 0.174, 1, 0.184, 1, 0.174, 1, 0.184, 0.21 ] + }, + { "time": 0.2, "color": "d7baff00" } + ], + "attachment": [ + { "time": 0.0333, "name": "muzzle-ring" }, + { "time": 0.2 } + ] + }, + "muzzle-ring4": { + "rgba": [ + { + "time": 0.0333, + "color": "d8baffff", + "curve": [ 0.174, 0.85, 0.184, 0.84, 0.174, 0.73, 0.184, 0.73, 0.174, 1, 0.184, 1, 0.174, 1, 0.184, 0.21 ] + }, + { "time": 0.2, "color": "d7baff00" } + ], + "attachment": [ + { "time": 0.0333, "name": "muzzle-ring" }, + { "time": 0.2 } + ] + } + }, + "bones": { + "gun": { + "rotate": [ + { + "time": 0.0667, + "curve": [ 0.094, 25.89, 0.112, 45.27 ] + }, + { + "time": 0.1333, + "value": 45.35, + "curve": [ 0.192, 45.28, 0.18, -0.09 ] + }, + { "time": 0.6333 } + ] + }, + "muzzle": { + "translate": [ + { "x": -11.02, "y": 25.16 } + ] + }, + "rear-upper-arm": { + "translate": [ + { + "time": 0.0333, + "curve": [ 0.045, 0.91, 0.083, 3.46, 0.044, 0.86, 0.083, 3.32 ] + }, + { + "time": 0.1, + "x": 3.46, + "y": 3.32, + "curve": [ 0.133, 3.46, 0.176, -0.1, 0.133, 3.32, 0.169, 0 ] + }, + { "time": 0.2333 } + ] + }, + "rear-bracer": { + "translate": [ + { + "time": 0.0333, + "curve": [ 0.075, -3.78, 0.083, -4.36, 0.08, -2.7, 0.083, -2.88 ] + }, + { + "time": 0.1, + "x": -4.36, + "y": -2.88, + "curve": [ 0.133, -4.36, 0.168, 0.18, 0.133, -2.88, 0.167, 0 ] + }, + { "time": 0.2333 } + ] + }, + "gun-tip": { + "translate": [ + {}, + { "time": 0.3, "x": 3.15, "y": 0.39 } + ], + "scale": [ + { "x": 0.366, "y": 0.366 }, + { "time": 0.0333, "x": 1.453, "y": 1.453 }, + { "time": 0.3, "x": 0.366, "y": 0.366 } + ] + }, + "muzzle-ring": { + "translate": [ + { "time": 0.0333 }, + { "time": 0.2333, "x": 64.47 } + ], + "scale": [ + { "time": 0.0333 }, + { "time": 0.2333, "x": 5.951, "y": 5.951 } + ] + }, + "muzzle-ring2": { + "translate": [ + { "time": 0.0333 }, + { "time": 0.2, "x": 172.57 } + ], + "scale": [ + { "time": 0.0333 }, + { "time": 0.2, "x": 4, "y": 4 } + ] + }, + "muzzle-ring3": { + "translate": [ + { "time": 0.0333 }, + { "time": 0.2, "x": 277.17 } + ], + "scale": [ + { "time": 0.0333 }, + { "time": 0.2, "x": 2, "y": 2 } + ] + }, + "muzzle-ring4": { + "translate": [ + { "time": 0.0333 }, + { "time": 0.2, "x": 392.06 } + ] + } + } + }, + "walk": { + "bones": { + "rear-foot-target": { + "rotate": [ + { + "value": -32.82, + "curve": [ 0.035, -42.69, 0.057, -70.49 ] + }, + { + "time": 0.1, + "value": -70.59, + "curve": [ 0.236, -70.78, 0.335, -9.87 ] + }, + { + "time": 0.3667, + "value": -1.56, + "curve": [ 0.393, 5.5, 0.477, 13.96 ] + }, + { + "time": 0.5, + "value": 13.96, + "curve": [ 0.519, 13.96, 0.508, 0.13 ] + }, + { "time": 0.5667, "value": -0.28 }, + { + "time": 0.7333, + "value": -0.28, + "curve": [ 0.827, -0.06, 0.958, -21.07 ] + }, + { "time": 1, "value": -32.82 } + ], + "translate": [ + { + "x": -167.32, + "y": 0.58, + "curve": [ 0.022, -180.55, 0.075, -235.51, 0.045, 0.58, 0.075, 30.12 ] + }, + { + "time": 0.1, + "x": -235.51, + "y": 39.92, + "curve": [ 0.142, -235.51, 0.208, -201.73, 0.138, 54.94, 0.18, 60.78 ] + }, + { + "time": 0.2333, + "x": -176.33, + "y": 61.48, + "curve": [ 0.272, -136.61, 0.321, -45.18, 0.275, 62.02, 0.321, 56.6 ] + }, + { + "time": 0.3667, + "x": 8.44, + "y": 49.67, + "curve": [ 0.403, 51.03, 0.486, 66.86, 0.401, 44.37, 0.48, 23.11 ] + }, + { "time": 0.5, "x": 66.57, "y": 14.22 }, + { "time": 0.5333, "x": 52.58, "y": 0.6 }, + { "time": 1, "x": -167.32, "y": 0.58 } + ] + }, + "front-foot-target": { + "rotate": [ + { + "value": 18.19, + "curve": [ 0.01, 11.17, 0.043, 1.37 ] + }, + { "time": 0.1, "value": 0.47 }, + { + "time": 0.2333, + "value": 0.55, + "curve": [ 0.364, 0.3, 0.515, -80.48 ] + }, + { + "time": 0.7333, + "value": -80.78, + "curve": [ 0.788, -80.38, 0.921, 17.42 ] + }, + { "time": 1, "value": 18.19 } + ], + "translate": [ + { + "x": 139.21, + "y": 22.94, + "curve": [ 0.025, 139.21, 0.069, 111.46, 0.031, 3.25, 0.075, 0.06 ] + }, + { "time": 0.1, "x": 96.69, "y": 0.06 }, + { + "time": 0.5, + "x": -94.87, + "y": -0.03, + "curve": [ 0.518, -106.82, 0.575, -152.56, 0.534, 5.42, 0.557, 38.46 ] + }, + { + "time": 0.6, + "x": -152.56, + "y": 57.05, + "curve": [ 0.633, -152.56, 0.688, -128.05, 0.643, 75.61, 0.7, 84.14 ] + }, + { + "time": 0.7333, + "x": -109.42, + "y": 84.14, + "curve": [ 0.771, -93.91, 0.832, -30.64, 0.787, 84.14, 0.799, 89.65 ] + }, + { + "time": 0.8667, + "x": 17, + "y": 75.25, + "curve": [ 0.903, 66.18, 0.967, 139.21, 0.932, 61.53, 0.967, 44.02 ] + }, + { "time": 1, "x": 139.21, "y": 22.94 } + ] + }, + "hip": { + "rotate": [ + { "value": -4.35 } + ], + "translate": [ + { + "x": -2.86, + "y": -13.86, + "curve": [ 0.025, -2.84, 0.067, -2.82, 0.028, -19.14, 0.054, -24.02 ] + }, + { + "time": 0.1, + "x": -2.61, + "y": -24.19, + "curve": [ 0.143, -2.34, 0.202, -1.79, 0.152, -23.98, 0.213, -14.81 ] + }, + { + "time": 0.2667, + "x": -1.21, + "y": -7.12, + "curve": [ 0.308, -0.86, 0.345, -0.51, 0.306, -1.63, 0.341, 3.15 ] + }, + { + "time": 0.3667, + "x": -0.33, + "y": 3.15, + "curve": [ 0.41, 0.02, 0.458, 0.26, 0.427, 3.3, 0.481, -6.75 ] + }, + { + "time": 0.5, + "x": 0.26, + "y": -10.59, + "curve": [ 0.553, 0.26, 0.559, 0.2, 0.519, -14.41, 0.548, -23.88 ] + }, + { + "time": 0.6, + "x": -0.17, + "y": -23.71, + "curve": [ 0.663, -0.72, 0.798, -2.09, 0.702, -23.36, 0.802, 3.53 ] + }, + { + "time": 0.8667, + "x": -2.46, + "y": 3.48, + "curve": [ 0.901, -2.63, 0.967, -2.87, 0.913, 3.45, 0.967, -7.64 ] + }, + { "time": 1, "x": -2.86, "y": -13.86 } + ] + }, + "front-foot-tip": { + "rotate": [ + { + "value": 28.96, + "curve": [ 0.056, 28.74, 0.049, 19.6 ] + }, + { "time": 0.0667, "value": 1.68 }, + { + "time": 0.5, + "value": -10, + "curve": [ 0.525, -10, 0.592, -54.69 ] + }, + { + "time": 0.6, + "value": -59.66, + "curve": [ 0.623, -74.54, 0.674, -101.78 ] + }, + { + "time": 0.7333, + "value": -101.78, + "curve": [ 0.812, -101.78, 0.855, -84.67 ] + }, + { + "time": 0.8667, + "value": -63.53, + "curve": [ 0.869, -58.38, 0.975, 28.96 ] + }, + { "time": 1, "value": 28.96 } + ] + }, + "torso": { + "rotate": [ + { + "value": -20.72, + "curve": [ 0.025, -20.57, 0.071, -20.04 ] + }, + { + "time": 0.1333, + "value": -20.04, + "curve": [ 0.187, -20.04, 0.285, -21.16 ] + }, + { + "time": 0.3667, + "value": -21.16, + "curve": [ 0.405, -21.16, 0.47, -20.9 ] + }, + { + "time": 0.5, + "value": -20.71, + "curve": [ 0.518, -20.6, 0.582, -20.03 ] + }, + { + "time": 0.6333, + "value": -20.04, + "curve": [ 0.709, -20.05, 0.815, -21.18 ] + }, + { + "time": 0.8667, + "value": -21.18, + "curve": [ 0.908, -21.18, 0.971, -20.93 ] + }, + { "time": 1, "value": -20.72 } + ] + }, + "neck": { + "rotate": [ + { + "value": 17.78, + "curve": [ 0.025, 17.93, 0.071, 18.46 ] + }, + { + "time": 0.1333, + "value": 18.46, + "curve": [ 0.187, 18.46, 0.285, 17.34 ] + }, + { + "time": 0.3667, + "value": 17.34, + "curve": [ 0.405, 17.34, 0.47, 17.6 ] + }, + { + "time": 0.5, + "value": 17.79, + "curve": [ 0.518, 17.9, 0.582, 18.47 ] + }, + { + "time": 0.6333, + "value": 18.46, + "curve": [ 0.709, 18.45, 0.815, 17.32 ] + }, + { + "time": 0.8667, + "value": 17.32, + "curve": [ 0.908, 17.32, 0.971, 17.57 ] + }, + { "time": 1, "value": 17.78 } + ] + }, + "head": { + "rotate": [ + { + "value": -12.23, + "curve": [ 0.061, -12.23, 0.191, -7.45 ] + }, + { + "time": 0.2667, + "value": -7.43, + "curve": [ 0.341, -7.42, 0.421, -12.23 ] + }, + { + "time": 0.5, + "value": -12.23, + "curve": [ 0.567, -12.26, 0.694, -7.46 ] + }, + { + "time": 0.7667, + "value": -7.47, + "curve": [ 0.853, -7.49, 0.943, -12.23 ] + }, + { "time": 1, "value": -12.23 } + ], + "scale": [ + { + "curve": [ 0.039, 1, 0.084, 0.991, 0.039, 1, 0.084, 1.019 ] + }, + { + "time": 0.1333, + "x": 0.991, + "y": 1.019, + "curve": [ 0.205, 0.991, 0.318, 1.019, 0.205, 1.019, 0.337, 0.992 ] + }, + { + "time": 0.4, + "x": 1.019, + "y": 0.992, + "curve": [ 0.456, 1.019, 0.494, 1.001, 0.483, 0.991, 0.493, 0.999 ] + }, + { + "time": 0.5, + "curve": [ 0.508, 0.998, 0.584, 0.991, 0.51, 1.002, 0.584, 1.019 ] + }, + { + "time": 0.6333, + "x": 0.991, + "y": 1.019, + "curve": [ 0.705, 0.991, 0.818, 1.019, 0.705, 1.019, 0.837, 0.992 ] + }, + { + "time": 0.9, + "x": 1.019, + "y": 0.992, + "curve": [ 0.956, 1.019, 0.955, 1, 0.983, 0.991, 0.955, 1 ] + }, + { "time": 1 } + ] + }, + "back-foot-tip": { + "rotate": [ + { "value": 4.09 }, + { "time": 0.0333, "value": 3.05 }, + { + "time": 0.1, + "value": -59.01, + "curve": [ 0.124, -72.97, 0.169, -100.05 ] + }, + { + "time": 0.2333, + "value": -99.71, + "curve": [ 0.326, -99.21, 0.349, -37.4 ] + }, + { + "time": 0.3667, + "value": -17.85, + "curve": [ 0.388, 4.74, 0.451, 32.35 ] + }, + { + "time": 0.5, + "value": 32.4, + "curve": [ 0.537, 32.44, 0.566, 6.43 ] + }, + { "time": 0.5667, "value": 2 }, + { "time": 1, "value": 4.09 } + ] + }, + "front-thigh": { + "translate": [ + { + "x": 17.15, + "y": -0.09, + "curve": [ 0.178, 17.14, 0.295, -4.26, 0.009, -0.09, 0.475, 0.02 ] + }, + { + "time": 0.5, + "x": -4.26, + "y": 0.02, + "curve": [ 0.705, -4.27, 0.848, 17.15, 0.525, 0.02, 0.975, -0.09 ] + }, + { "time": 1, "x": 17.15, "y": -0.09 } + ] + }, + "rear-thigh": { + "translate": [ + { + "x": -17.71, + "y": -4.63, + "curve": [ 0.036, -19.81, 0.043, -20.86, 0.036, -4.63, 0.05, -7.03 ] + }, + { + "time": 0.1, + "x": -20.95, + "y": -7.06, + "curve": [ 0.162, -21.05, 0.4, 7.79, 0.2, -7.13, 0.4, -1.9 ] + }, + { + "time": 0.5, + "x": 7.79, + "y": -1.94, + "curve": [ 0.612, 7.69, 0.875, -10.49, 0.592, -1.97, 0.917, -3.25 ] + }, + { "time": 1, "x": -17.71, "y": -4.63 } + ] + }, + "torso2": { + "rotate": [ + { + "value": 1, + "curve": [ 0.006, 1.2, 0.084, 2.88 ] + }, + { + "time": 0.1333, + "value": 2.88, + "curve": [ 0.205, 2.88, 0.284, -1.17 ] + }, + { + "time": 0.3667, + "value": -1.17, + "curve": [ 0.411, -1.17, 0.481, 0.57 ] + }, + { + "time": 0.5, + "value": 1, + "curve": [ 0.515, 1.33, 0.59, 2.83 ] + }, + { + "time": 0.6333, + "value": 2.85, + "curve": [ 0.683, 2.86, 0.796, -1.2 ] + }, + { + "time": 0.8667, + "value": -1.2, + "curve": [ 0.916, -1.2, 0.984, 0.62 ] + }, + { "time": 1, "value": 1 } + ] + }, + "torso3": { + "rotate": [ + { "value": -1.81 } + ] + }, + "front-upper-arm": { + "rotate": [ + { + "value": -9.51, + "curve": [ 0.021, -13.32, 0.058, -19.4 ] + }, + { + "time": 0.1, + "value": -19.4, + "curve": [ 0.238, -19.69, 0.337, 7.78 ] + }, + { + "time": 0.3667, + "value": 16.2, + "curve": [ 0.399, 25.42, 0.497, 60.19 ] + }, + { + "time": 0.6, + "value": 60.26, + "curve": [ 0.719, 60.13, 0.845, 27.61 ] + }, + { + "time": 0.8667, + "value": 22.45, + "curve": [ 0.892, 16.38, 0.979, -3.27 ] + }, + { "time": 1, "value": -9.51 } + ] + }, + "front-bracer": { + "rotate": [ + { + "value": 13.57, + "curve": [ 0.022, 9.71, 0.147, -3.78 ] + }, + { + "time": 0.3667, + "value": -3.69, + "curve": [ 0.457, -3.66, 0.479, 0.83 ] + }, + { + "time": 0.5, + "value": 4.05, + "curve": [ 0.513, 6.08, 0.635, 30.8 ] + }, + { + "time": 0.8, + "value": 30.92, + "curve": [ 0.974, 31, 0.98, 18.35 ] + }, + { "time": 1, "value": 13.57 } + ] + }, + "front-fist": { + "rotate": [ + { + "value": -28.72, + "curve": [ 0.024, -31.74, 0.176, -43.4 ] + }, + { + "time": 0.3667, + "value": -43.6, + "curve": [ 0.403, -43.65, 0.47, -40.15 ] + }, + { + "time": 0.5, + "value": -35.63, + "curve": [ 0.547, -28.59, 0.624, -4.57 ] + }, + { + "time": 0.7333, + "value": -4.59, + "curve": [ 0.891, -4.62, 0.954, -24.28 ] + }, + { "time": 1, "value": -28.48 } + ] + }, + "rear-upper-arm": { + "rotate": [ + { + "value": 28.28, + "curve": [ 0.034, 30.94, 0.068, 32.05 ] + }, + { + "time": 0.1, + "value": 31.88, + "curve": [ 0.194, 31.01, 0.336, -0.11 ] + }, + { + "time": 0.3667, + "value": -7.11, + "curve": [ 0.421, -19.73, 0.53, -46.21 ] + }, + { + "time": 0.6, + "value": -45.75, + "curve": [ 0.708, -45.03, 0.844, -13.56 ] + }, + { + "time": 0.8667, + "value": -6.48, + "curve": [ 0.909, 6.59, 0.958, 24.21 ] + }, + { "time": 1, "value": 28.28 } + ] + }, + "hair2": { + "rotate": [ + { + "value": -2.79, + "curve": [ 0.074, -2.84, 0.121, 25.08 ] + }, + { + "time": 0.2333, + "value": 24.99, + "curve": [ 0.35, 24.89, 0.427, -2.86 ] + }, + { + "time": 0.5, + "value": -2.8, + "curve": [ 0.575, -2.73, 0.652, 24.5 ] + }, + { + "time": 0.7333, + "value": 24.55, + "curve": [ 0.828, 24.6, 0.932, -2.69 ] + }, + { "time": 1, "value": -2.79 } + ] + }, + "hair4": { + "rotate": [ + { + "value": -6.01, + "curve": [ 0.106, -5.97, 0.151, 18.62 ] + }, + { + "time": 0.2333, + "value": 18.72, + "curve": [ 0.336, 18.7, 0.405, -11.37 ] + }, + { + "time": 0.5, + "value": -11.45, + "curve": [ 0.626, -11.46, 0.629, 18.94 ] + }, + { + "time": 0.7333, + "value": 18.92, + "curve": [ 0.833, 18.92, 0.913, -6.06 ] + }, + { "time": 1, "value": -6.01 } + ], + "translate": [ + { "x": 0.03, "y": 1.35 } + ] + }, + "rear-bracer": { + "rotate": [ + { + "value": 10.06, + "curve": [ 0.044, 11.16, 0.063, 11.49 ] + }, + { + "time": 0.1, + "value": 11.49, + "curve": [ 0.215, 11.49, 0.336, 2.92 ] + }, + { + "time": 0.3667, + "value": 0.84, + "curve": [ 0.416, -2.52, 0.498, -10.84 ] + }, + { + "time": 0.6, + "value": -10.83, + "curve": [ 0.762, -10.71, 0.845, -3.05 ] + }, + { + "time": 0.8667, + "value": -1.34, + "curve": [ 0.917, 2.54, 0.977, 8.81 ] + }, + { "time": 1, "value": 10.06 } + ] + }, + "gun": { + "rotate": [ + { + "value": -14.67, + "curve": [ 0.086, -14.67, 0.202, 8.31 ] + }, + { + "time": 0.2333, + "value": 12.14, + "curve": [ 0.279, 17.71, 0.391, 25.79 ] + }, + { + "time": 0.5, + "value": 25.77, + "curve": [ 0.631, 25.74, 0.694, 4.53 ] + }, + { + "time": 0.7333, + "value": -0.65, + "curve": [ 0.768, -5.21, 0.902, -14.4 ] + }, + { "time": 1, "value": -14.67 } + ] + }, + "front-leg-target": { + "translate": [ + { + "x": -2.83, + "y": -8.48, + "curve": [ 0.008, -2.83, 0.058, 0.09, 0.001, 4.97, 0.058, 6.68 ] + }, + { + "time": 0.0667, + "x": 0.09, + "y": 6.68, + "curve": [ 0.3, 0.09, 0.767, -2.83, 0.3, 6.68, 0.767, -8.48 ] + }, + { "time": 1, "x": -2.83, "y": -8.48 } + ] + }, + "hair1": { + "rotate": [ + { + "curve": [ 0.028, 1.24, 0.016, 3.46 ] + }, + { + "time": 0.1, + "value": 3.45, + "curve": [ 0.159, 3.45, 0.189, 0.23 ] + }, + { + "time": 0.2333, + "value": -2.29, + "curve": [ 0.265, -4.32, 0.305, -5.92 ] + }, + { + "time": 0.3667, + "value": -5.94, + "curve": [ 0.446, -5.96, 0.52, 3.41 ] + }, + { + "time": 0.6, + "value": 3.42, + "curve": [ 0.717, 3.42, 0.772, -5.93 ] + }, + { + "time": 0.8667, + "value": -5.97, + "curve": [ 0.933, -5.99, 0.982, -0.94 ] + }, + { "time": 1 } + ] + }, + "hair3": { + "rotate": [ + { + "curve": [ 0.067, 0, 0.159, -10.48 ] + }, + { + "time": 0.2333, + "value": -10.49, + "curve": [ 0.334, -10.5, 0.439, -0.09 ] + }, + { + "time": 0.5, + "value": -0.09, + "curve": [ 0.569, -0.09, 0.658, -10.75 ] + }, + { + "time": 0.7333, + "value": -10.7, + "curve": [ 0.833, -10.63, 0.947, 0 ] + }, + { "time": 1 } + ] + }, + "gun-tip": { + "rotate": [ + { "time": 0.2333, "value": 0.11 } + ] + }, + "muzzle-ring": { + "rotate": [ + { "time": 0.2333, "value": 0.11 } + ] + }, + "muzzle-ring2": { + "rotate": [ + { "time": 0.2667, "value": 0.11 } + ] + }, + "muzzle-ring3": { + "rotate": [ + { "time": 0.2667, "value": 0.11 } + ] + }, + "muzzle-ring4": { + "rotate": [ + { "time": 0.2667, "value": 0.11 } + ] + }, + "back-shoulder": { + "translate": [ + { + "x": -0.18, + "y": -4.49, + "curve": [ 0.133, -0.18, 0.333, 7.69, 0.133, -4.49, 0.333, 2.77 ] + }, + { + "time": 0.4667, + "x": 7.69, + "y": 2.77, + "curve": [ 0.6, 7.69, 0.858, -0.18, 0.6, 2.77, 0.858, -4.49 ] + }, + { "time": 1, "x": -0.18, "y": -4.49 } + ] + }, + "front-shoulder": { + "translate": [ + { + "x": 1.46, + "y": 9.37, + "curve": [ 0.162, 1.41, 0.333, -1.66, 0.162, 9.37, 0.301, -7.23 ] + }, + { + "time": 0.5, + "x": -1.6, + "y": -7.27, + "curve": [ 0.735, -1.5, 0.847, 1.46, 0.723, -7.31, 0.838, 9.32 ] + }, + { "time": 1, "x": 1.46, "y": 9.37 } + ] + }, + "head-control": { + "translate": [ + { + "x": -6.46, + "y": -8.4, + "curve": [ 0.053, -5.31, 0.167, -3.64, 0.093, -8.4, 0.196, -3.81 ] + }, + { + "time": 0.2333, + "x": -3.64, + "y": -1.32, + "curve": [ 0.309, -3.64, 0.436, -5.84, 0.275, 1.43, 0.38, 10.3 ] + }, + { + "time": 0.5, + "x": -7.03, + "y": 10.29, + "curve": [ 0.538, -7.75, 0.66, -10.54, 0.598, 10.27, 0.694, 1.56 ] + }, + { + "time": 0.7333, + "x": -10.54, + "y": -1.26, + "curve": [ 0.797, -10.54, 0.933, -7.91, 0.768, -3.79, 0.875, -8.4 ] + }, + { "time": 1, "x": -6.46, "y": -8.4 } + ] + } + }, + "ik": { + "front-leg-ik": [ + { + "softness": 25.7, + "bendPositive": false, + "curve": [ 0.008, 1, 0.025, 1, 0.008, 25.7, 0.025, 9.9 ] + }, + { + "time": 0.0333, + "softness": 9.9, + "bendPositive": false, + "curve": [ 0.15, 1, 0.383, 1, 0.15, 9.9, 0.383, 43.2 ] + }, + { + "time": 0.5, + "softness": 43.2, + "bendPositive": false, + "curve": [ 0.625, 1, 0.875, 1, 0.625, 43.2, 0.846, 45.57 ] + }, + { "time": 1, "softness": 25.7, "bendPositive": false } + ], + "rear-leg-ik": [ + { "softness": 5, "bendPositive": false }, + { "time": 0.4333, "softness": 4.9, "bendPositive": false }, + { "time": 0.5, "softness": 28.81, "bendPositive": false }, + { "time": 0.6, "softness": 43.8, "bendPositive": false }, + { "time": 1, "softness": 5, "bendPositive": false } + ] + }, + "events": [ + { "name": "footstep" }, + { "time": 0.5, "name": "footstep" } + ] + } +} +} \ No newline at end of file diff --git a/spine-sdl/data/spineboy.atlas b/spine-sdl/data/spineboy.atlas new file mode 100644 index 000000000..b07ccc3bf --- /dev/null +++ b/spine-sdl/data/spineboy.atlas @@ -0,0 +1,101 @@ +spineboy.png + size: 1024, 256 + filter: Linear, Linear + scale: 0.5 +crosshair + bounds: 813, 160, 45, 45 +eye-indifferent + bounds: 569, 2, 47, 45 +eye-surprised + bounds: 643, 7, 47, 45 + rotate: 90 +front-bracer + bounds: 811, 51, 29, 40 +front-fist-closed + bounds: 807, 93, 38, 41 +front-fist-open + bounds: 815, 210, 43, 44 +front-foot + bounds: 706, 64, 63, 35 + rotate: 90 +front-shin + bounds: 80, 11, 41, 92 +front-thigh + bounds: 754, 12, 23, 56 +front-upper-arm + bounds: 618, 5, 23, 49 +goggles + bounds: 214, 20, 131, 83 +gun + bounds: 347, 14, 105, 102 + rotate: 90 +head + bounds: 80, 105, 136, 149 +hoverboard-board + bounds: 2, 8, 246, 76 + rotate: 90 +hoverboard-thruster + bounds: 478, 2, 30, 32 +hoverglow-small + bounds: 218, 117, 137, 38 + rotate: 90 +mouth-grind + bounds: 775, 80, 47, 30 + rotate: 90 +mouth-oooo + bounds: 779, 31, 47, 30 + rotate: 90 +mouth-smile + bounds: 783, 207, 47, 30 + rotate: 90 +muzzle-glow + bounds: 779, 4, 25, 25 +muzzle-ring + bounds: 451, 14, 25, 105 +muzzle01 + bounds: 664, 60, 67, 40 + rotate: 90 +muzzle02 + bounds: 580, 56, 68, 42 + rotate: 90 +muzzle03 + bounds: 478, 36, 83, 53 + rotate: 90 +muzzle04 + bounds: 533, 49, 75, 45 + rotate: 90 +muzzle05 + bounds: 624, 56, 68, 38 + rotate: 90 +neck + bounds: 806, 8, 18, 21 +portal-bg + bounds: 258, 121, 133, 133 +portal-flare1 + bounds: 690, 2, 56, 30 + rotate: 90 +portal-flare2 + bounds: 510, 3, 57, 31 +portal-flare3 + bounds: 722, 4, 58, 30 + rotate: 90 +portal-shade + bounds: 393, 121, 133, 133 +portal-streaks1 + bounds: 528, 126, 126, 128 +portal-streaks2 + bounds: 656, 129, 125, 125 +rear-bracer + bounds: 826, 13, 28, 36 +rear-foot + bounds: 743, 70, 57, 30 + rotate: 90 +rear-shin + bounds: 174, 14, 38, 89 +rear-thigh + bounds: 783, 158, 28, 47 +rear-upper-arm + bounds: 783, 136, 20, 44 + rotate: 90 +torso + bounds: 123, 13, 49, 90 diff --git a/spine-sdl/data/spineboy.png b/spine-sdl/data/spineboy.png new file mode 100644 index 0000000000000000000000000000000000000000..d1c3ac1be1223bbd5db68f0d66013461a0af8c4c GIT binary patch literal 243396 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

S4s4p2G(aURPP;@lUvl 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

IrQ$Pv%KkU7Em?ddh z=KXu#Sk9JvR@UC#)zv%A&VUM^fZ{qJE!yD7DC7E#gXpNEGo#-OFb?yL8#p-NxO_T% zj4TQYDu@e$-~tRw)6g{CUES5SW>w{$C(j=7zR&y-5$8mlb8@MoT9&Kgy7J1*vqZ#+ zcq7hx-_QNrH!XpE8+FpN$022S$;ak7Iw4(~);kU%+ANsv@5%0rB%!hyr zs_r_)9ou)qH;o1MvE{T8?m>uEd4@6N!`rRjDH|ZC1&liPxC1x7IN0MMcmTLBin^?C zF41bXQfHB3)juQgRb8J1h~kKLx6P?jXL!cbpL@r1p8M>7ca^}yQ-HSu2T}#VHv=EO zTK)RzdOn=4_x1f8wjXv(CmP zrBc7|Ueyst19ejz^@zHiTxW%0V{Mu5IkLbDuRlW9*)el`MVw21R0}`5$w9j* z`S8*wK7qfw`-S|(LWSn%KE~$qMNCc+kR69a8Q`!{kJjcI8yC;9e&G!5#wO0j{j`EC z;xF?_$P@#rj=0w)?)AR#i@|ury$jG^1yV(siB_~q*07>LkqB)$K& zxdk5jyZ8OY`uRuyXpduiJNdsiuiXgY}*idDBT;Ko5V>`+0bN>%302oy6 zRr9fdZb+fv2J6ZJ{~~d)oi~9S)4nk5ci_gi0_2kl1}Xqg`f+wS7k#CicrOgbfu=Em zgK?vSv6Q^dj3fbQzu!Oe1u!|&K!Z{Mt(qeU zGTFNhOWbo{SaYCQQx!noLm>U_XVrC8%~$!oRHpA`fIUsE$l3w|(h9$3ReQ~o1?lss zKOh6hfPaAqAh+E?Twwvc4}*L!{mkCr^Wo)>L#B#I6&3>(KpyOC!N^eZF^8I~{1;j` zv;h8xAK!KPfge_hBAx)u0Dqg_D=q-DB5W+J@hsotJwXLLLk(d{lj>h2NvpuxS|^F5 zv=N@dzM#Yn7w_le0I&E(4cc8MJ;UPzj~4|2ZwdV*hSUpax`>T$`eB_eg?wWaGZpyU z7{~nW$~u3!UT5jpbx^Hv%ro5kTR$(SKl+XY`2YADR9^85>i^h(*7A@47p+bDRH~NJ zky{A<<*U{Ip*QMf&y{}b)#85RRkHP)ua<{A!)Kj`-|OJYwdr$ zR^p8*Hp(@6UXs-CdkA&4Ff+;YMx9OHV{2xLc}+U~0#%GL1Sc=?uoyZ%@(E0A^^ne7-Np9 zx*EpV&c>;Y;n&O^`c|JLUh$u#>atM8d@|~Gx31)S`|wRaQdZUHxj4FARj&uF8Y3QF zSl@W~5B|}j+m_C~Bfj{^@AsBZ{PDJvq_Ypcvn|s5ZQ##Q4m#JpTj>4J;o~pcm1sGC z;`h7adH*2deOf@(c4xxUzKN+LFOgB@z#mxSRXzrsK{;q$_ZbIo{GPGbaAR{R5-}$c zoT|OS^SFNg=(p_Udbc3I3%HH?GL8Bsy-`i{=P@xoLuGB-}J_K4)~sQI|qR`r`x}_TwAUY0ADzj zr$cr0VrF-C#TyYk@d%7ZARZQFx!0nX1Gdh&49vBz_HNoZE{C-VuP;>|Q@M85_NUBZ z46V&IdhHfyk{HlfTk3aQD~RVMk$ws$2oQwE>JqKZHN=>H93Ub@oz_5?n^pgF z>+5#gJV`tJ(Bp3HgZ1oqXQ01u4u)jfS;=lUBA{n2u~T01_&fQlyYJ%r=1Q!5=A&$_ zE*CrkGSWgsFeWvzc8=D@TB;5>&H9BiY+N|Y=Ebwrmo8Ahw8+MVGpwCI#nx()cHoEM z6`w;fNkZ1jnG-CX{5+M3X(ksAqv|l8M}6r$Yv)cTagN#X5uvlS!NQHVGjrq^tEV4o zJousa{ODznTRQZ(8HxxTHUZ3o|Lk`-L;{Qh|GUcm^UJfpMMaEe@^K5=1(z9zl~Pl= ztje5$4sQ^v(+6{7^gBfZzyatT_q>h(?4o?0J+%X<4z~p4s_=|!0dONN0C^R_fCzB? z_37C30ymUeFx1Y_Cj^SE00kvPUj;y#SFb)~&iF{`yHEiwt5Ql089fvq)Dy)dARmzd zIFw=ng?dQ^o|39g$CK>hgsFpSf96#%AT4rL{+zVD-x4tVTcyzBUq}WZtynXp3@Ee; zc@cF{)e9TGtgZm1~PAe4EVd$AHaF4c>tfO0|xB^sv2n0Vv+0nPoGPA`TY#s zZ{t%?XaUGQ-zNd`pPj7tzfX!F#rBDO??2;OD`fQE&_7@K>n`60kZi_7^YdKnCy4PX08#B?dgcgEsgx752slimc$m4!Rueh50>#vmhZ@fxA zUh=um6K+z+t!ABlKlU2!S6koulcKNxx6&<5)1^Y&DSxJdO$(~gg%vg@t4J77b4noK zIYmm2(1&-6;W;Q#5(i3@nx3ts=c1Py`b~uaPxgI&f2+ma-a35Zy`t*40BE;IY%O(x zmOx3B$(R4XdnZ5qt}7YQ$BD?Kh@4tq`RK?*p)@g5QX6@|*Q8%dpB3@XEuH?uZL72g zj(ul&;ii8!weQC7D6~<`?Eh!R_?!RK+4xR`8xYow@z-JU#NtD5-TG3T+FGiyG2S~7 z-is2txcf}w^KG*SzHV0+;&TtaZNqr}=M~PQIs*p`9D^83}9@@hI(W>I~rgp=ikTVA^Bq{{dF-!Wnyh@viA%?;}HI?h?tWkh?C z1W@M^LqJVh0ov^rjjaYxeaf@vpZC14`-v~`(7rbFlK{RyCBYucO64Ws`_t{+nQs5u za&5Ur09-kV*Dnu`UT#AVMe0~=Epy-cBfNX@UVic9-|}xC{A*r$;scy)uBM=0A6pYZ zbqisomKdKnH(G8L!NpPkaTiB)wl=Z7E*L|%+2~j3ljywC@CxWPx9GK7$#d%1Tslv; z)c|z_rD}3xl_X=T)7ZrLL7(`@OWBR*(Y$nypM1&_uvuF|2K-gQnB<3nRKFnIA4al! zXshU1i&N!?pOh#9UQkso-un@n>#Kc5fQ=$LjZM0(Mq+@Nwkaf@29h$yF*c5{aSv-_ zf^voFgGZU(e}tf1xnkfCu(5cW#ry9;75uQo?DaRLP|dXgl!yBNw=)2gG20$FdAGjc zbuxFedc8|3Zbn z=$7_!gam-`o6B8JHUoEcR1bKVh}Dl2rj zsz21Gp<{hQ3QD2G|6%84ro!RKQUTUbnws>JI}yB@otU8Y>#vk3y@$=G1ZK}V zHZ+OfV&)QJ4YeTTY3I)KVSxYpm0E5$DXZ{LfPWf>{C8k74auqBcR$7mcLV`9dj{hf zTGsL5C}M4^LAO$424w}<`mtZq$$$AOUFbELiY;?$m=+}zgNMbl>1Z|vVnMK&@3;zG z%5Qk3T=bTBe<`G?>ZQg)gxG_LJg%>)(s*PO??0@Tnj~HaD=ohrO`EseQ8? zbII){NYy*03-xk^v+B5|T4vKJC51KML4jU+H#wy+D{#kTjeAsie4KorppFeOw0aRX zjuRzDdR}IF-MxRlO3mEloSPTp`Q6s)Sf~5Qx}M^min1Rx6y*J_Zqy!I-M{mD2@3M`$=UMLTpT?|6BEfZV*F)H<&pCze&9#rj=tW-kvLE=aa;f&5D+CVTMq;Ab0bZWA9{po1XVUF;fV*-yMbrqe-wR5T zRrf|B0LAMY@EPOLSY0CSw9`m$rM0orm;8G{n1JFT0-@X5qBgsLbB@i6=drzR@*XjS z)mpM$Adb2?>yiq4N;c#{Yjb0rZ$7k-nu;JT@+z;o^h$5>{(z{G#iR zP}}QLn_FOF-vOo%UZ`>>LA!xB*uOrgk?rg_^OgT%1mEc6BXamLtpfS#eBLN9 z5601<*JsKA#+d#w^gH*wGV0c^It=#RHv5wP}9udfIg^uZT>0o2os9LUED4#2-xFyk9`@XsX!+5x%9t8YDwVJfzsW6oi03Be-_;i*oum<3KJm5}axc@VMB0T`# zXDW`kK^=D*Ls%*CktE@QUdyuO;JPs6HdWTbfKZiB1RiIfdI#}WKZW@?=73XXW5*M^ zEuM^UE5Z?k2~DybQHMIgsY?Wg2E0lL^+nuken75D0pSgAl-{rXocyg{pxA7{=xrQ|5BfLpMU5btDYA=BF4N2;XXxiaqH%zx4vxOuJ1kh_O6I| z1d(+oQWB&TyJ)6Vs?Cqqr??;@52(@xJ3D{mMdQr?Prb`3vIr~#(}>>aiA)9R&rKbA z{*LG0s(r^qb$T<3du-O1>9)JHnymtv?s5qMuI~rnu-4M)w7GO?iQ~tfQ2X{5J^!Uw z0RBVZ)#*Ixw}HQr$0+e@%mhw>zXATgbo;MP_vhMjZMjAOTzO?(cNGaFQy7m;sqh(@ z=e=?rf8!nFAI$Y!q>=*I!~-A&a95oG2+OsDSp{0HOQV)^$V(*g07!v+)M;UDjEJGR zvD!~3P<4s?KP4<|+Q+ZXF?HZ5BEr_{MXZer&ibD3CnY#B^jZ!4AVehTkB>U-L1vc- zZe@{|-*F3(O+biPo6HEUMWP6?Y0tlan56IC_#R#+5V@JjcexvurG$ zW@GU*tz~*lhaJh?`P)Vb^6)&{r>-Y@Wb!@fvW)ick%(qT-`J9za0TE zoF=dn5is_o#j#}D%VeY(HuW>}Hc8q55-)(nbgnc2ANjbB9MByx{~Lh(af*QP)qe_x zd86@pqtStbs{f$U&yW#df1YwjR=_#53=0Lg0$@BdzF(3v0o@PWq(Gbup`T#huZzecQ z1PtQ;JRSHIC%mPUj30=)ERH1wdX%F~?k)nUjx)ZwLow!V0t1IBPTwygRdbt7C1LQYgeSl!~5 zUc^&?!varH$5RAuQaG$&QF>`FdJiEs$*1>xFkg5?)ulew?8ag_rjVW*MSi< z_}sR0COHeM?k-h(JiYhb zV@&zf*$3XV_T@79ZqzR<8B;zDd=yy5Ij0&OT)6RtyB_Oar00z>%Ldwts|Y%&+C4UR zsF-@t*u2<8f<@q>Iwniiu)6(>#9K*yBRarQg^H@1o~X_27!BBJ)z2#WnSFDIXtXxy zMs1pnEqc9PANYd~MFOTO0CjYG9a^m|&YoH3g?HWc0wbk`%RH3d20Sq(FMe4b1Fx@L zCUPEX$TbzfwdEQCaAmMRUrM+fB;}ALV8IIZo*JH*Eemy+B@uUB`*m3 zAbwmWI_`FpxVX(#;!Y=xlj|f#bGg?CrNovu>a=ljRQP=D`UY{gTX=mUp*Q^sXs^>w z)*%UT&}nY<=W)%=bzXR6frWCIl>0K-zqM(LcWTP#8B7@9l}h-vGU3c5;l5c)3p14F zrU<7e@M;xIDI6sPF5{fFV#|#0@w(?clXu_sEj%OI^>a=LrH`u&%jUp=903@b7l+)Fx>xN$*BR#IUfKomj=K#F@S}AP%TdCj(yb!#l@M# z94lyQ{wG;`Le@KwfzL1nJmG+J7^-HERPpnd`Z%dSV|)L{#t9Z4W2E9fSic)03tT@D zVBiU`oe7{0Oa*hE0h%)d%+@#T@?T6aV62*9)bk+s`HRO-22b{t0vW*dQ>TBEMl=qT zBa%AQr*V9eC^%9|L7n8H3lvJ~sD0ht+x7hF&^XSb9*e+#ObWoTvwW`d->+1gTxDBw z{s5Az^yfSR3jOU~!6QJ){?Drba;*V*zNIXIVALaP6i$&Jf7?!%7*yj3|qhPA0&D#Cp^FYN_ntUrd|;aHnu4J#II_3-0PNS=uJ!~ zUI0c2T5TRUFvp$h01tCvkzRS8vbOn%7jkE_N=Lw`(ghm82FfOIFA2YYeYMP*!u;~X z3F+sWG(~8|F_NYMY^8Ue+8@7uclAT5HhGI1wXH4mn&Y8t8r^)yFg}+jD6X!fH zypSqpRMo0=rCnY4mmYmvq{tbIY=YTDwWiKqSE*HJ@~_)m{%bYHzX;S-)ff|(w_E3h z6K`!B6FxxFhBGT@71e3P?%Mp)Xl?vX;FqHL*~6@@Uqn^eO8o%(B!CWx_Utj|EH<_@ zn++~5E^*88C+&asbH3(gjG|tAjIO_NR$59uxai1svc|eB0h*mH1lACWNvM#CkXTU!N zB4P~U8T?v>aDJNF@gvMU^$zB~`ti&@?RI9Ld>b=QzJ=*Kk287e_0(=WO!?>m{Ml(F z4AaiwJQ1)rGN2j994dvp_Bqeu{V)1fUSQg+-}k@i#xdcqeH!B5Bc|Mk0m>ddD<@^gC^wCGN5{IX3PU`}FRhyX07Kc#{BUZDlR zjH%4(ZfybcQ%@E#;wNTmSWR3lT|eJ_AkAh-rI4>o7OuSlkY%XqKcom42FLvZIl8T5 zzUHdvIs&Fg9r?-k^pmgf7bhusvS6f@V4Q3Jxb}sD!JixYtUD1N6pukxLD%BlWnlc4 zx&12x@SnNb_ghIa&~8LAX`$Dp_e4#9d(Kgcl`UuepBtzDIz|Ck1$YBSjr?-S=`JKi|LKm;0m6>qq)!=79b7 z0axhnFUb6JDF#F4e}x2tLS$bb$QLX7LzVubN+3^~DBB!UOwOn=PtxScrR@|Q{l9ne z0SKoEY)lF03N$tO@quV*jl)JbYeKpJkzrPqBZ>C}b*DtW8<=(}JWE181n}~^RAS4d zDmOai8J@>Is%)sBMu=9{xj}>pRaW-R;s*iN80reQqC5e(P7;EwmKvD$fcp{Ffrcm@ zg-8^O#j}Ri8(t~(cmIU^qd3U_+Rw=Y)oDT(Q*sSzKl95;OyGC@w3dGM=MztkH-EqA z>?GD&tO)V)I`tr6&NKJ|Mgp`FL+d7%ydLGSfrrp_$=T`vbpb1hEBq;hQmw*Kl#}b5 z9Dl{D)coo1iZjBhQ~W5V>y#}48|n_Kp#1&bMz-7tkrtX@P~>)bn;phvDt3Yl|N)murM!nQpI*jbfUO zMgsV4k#tYI0g@l6csw_|t;}LZ`h>`y5jpy|<>$9a7 zMcm<84o^-(2L1~m*dRs-rfbx0Jk0FV?qK#AcQSeV4OEZrr*d$f%E39RNB1*v(-Ed` zzk%u7Z(#EHA!^qjpmKPDU~U>Ig*o#;_S_zzDG^ZheSYSvpUwyG{x*JO+R(fI6Krm* zQl6T{3qpGBCf%(~T--y9At+S{D>eL56)y9VB6tn zlIIui|Kz70{rKNJWA*fj*IyN)YqUzAH}msy3V>nIU&$!|{Gs=ZtMKi*EF7AV*r}f$ z7Aqog${U!%fF4MZH3we20oK=TipSgaxHFm%FsvdNu8eCjJwUhd2pDMz7*-YJo%}O- z|Bx|Y(Q9DL`WcP^)G<~3JS{lR3t^aCVB|F_eS^geOlu1Af)wm$&-pqS4@h$UJQ?)S z3uv-{MKU)-P^C&u{XK}Zwi{Qus_|f7hX8-BJ)j>MnXC906aZ#8!fwbjAa58b1+M_| zPWXd%0M&d-Kn|GaVg&ou{C<1D=MG_ip!GdA-M81@0kj2;Ijj_9r$CgTXwc(D2vbm9tF;#G*#(F5Q)M09#WlT>vvuyt$*z4&bjk^ zZgPqP?HJeYCdPd8Gwj>2-2Bt8OLcRUYO^&vq-Opob%`&xJcEj za1x(`B6N(y2bz+o0BQE5sT)<}BMC`l{_5OAZyP&}3xnwer}0sRgM!(NyDR-T!LVGr zNnIoemjv@JG5$S^C*Iona-D=&X5y@Z%G(AxCDZ`gL;fA+yQw-HW}jFV~9)qvU3 zWNEj)_NW(k|Ks|`rRKuiQOcz%2(+6`I_)+N#oELW&{>-#1!U2H&Jjgj>YE!Zo?qhY zzW#Ynny6Lo*yG`QZaNqGG)b$=mCnZ&fsX+XeF0|SkE9BlO1fXymTSv30^l+hx03=u zMqVr6?Hoa;o*6ST)s~L2(9%Mo^$v>vS%aU zUl1esQ#B@TJI2%#Zl-*6KVBv5@1qDwYe4D;;8)9pvy;>g?_=`%15Dg-fQe%Vsa|)G zaA6KpE){|QW%vYi;zXqJ&!6~s-v5%j_^-DeW$x5{TzdHP#LgvE`ceh8mZ;OB+iDPZ z+o|E9W7k>g<$g1mghblBaCZICkALvzmLIwQSuT$5xeDN4w)vYO6+or{$iRP?BLPZ7 z1b}Q40l24w;!mick{Pc#Rkf)PeYUPlPHsS*CKY6(NV`zK&G;86*EM zk_XB*Mtc-{>GKHzBLF++^gl5B8~(|X23#>tkYRa$o@B^Wp+_~V z-jIJ1G3hDTorn# z|38fo)IvmHQ4N#1*AL#WW(f2e)iKe7nId2~K5#%>c{vlooT7k{3O;G&Uvn)16xsk1 z)j^^bpa}X4CVpy2RSDU;MHyBB^wSV>6@M8E{&uq~?utC)dM}SF$m0nE6Go08=N9*&yUuz2UfB(Pa zG(c^VnK-8GlqVZQPlSsiw8Y>x>)fCYPhoj}hR9DmEV>H&fGHA!tCrqBd%&u|XAxTJ z=*AwA?K7ap)m${aJ}RKIP^zr+vc#gxCpcg-9Vrh*7Fe`8Rp3x{ zGMNink1Y}6UJN#A1E@Rj_66+d249{yGOxr_Vl;}oc3T;?s@{PT1D`W~_2F@^ncDw- zqS`$KJT4t0XP3^r&m}~|*NbYjfYJi_xETNQ=O21YhcEM`)2d(cJpZh7G!jK72b}87 z*WL16lY5+2JSs>BSU~hJXh~qopmV-5ClW3K8_rp&))vY;Pf*II6|Dn-KvkWcDOdJY z_plQm*=nx8rPFH;z6wOBZ*CIz5<_R_l2jgy~mFELr$S9|5F{^K+D zc<8<+o%{TKs&u)Mowy@PKDtgO}HqYXrdM5&&{JReIT`1U2~U z*v((V;p?8XyJ+Bw34q9HE>drJ6JiW;ubZ%(-431l`nG$a&LzG8se^yiZPOX@2^bHE(CbMXYPR$=ni8<@QH27>8{WP7?3 zD3*i_D3=IlC#W8tXX1tfOx<*dshbWnar_A3z8S>##}fg2AOm{V(Ty$7x$Y=`aM$zs z({KJ-?(*8S?)w;*PMsijmQrns^29WLsoby7+c?6xc#wW@r58r(Z%|)4-#-1x5C8Vr zd;j5y?aj6S`UQN+Y)1wRlK{hQ0R9lz4{|@@kO`nS49a$k{wrcUNh+hsAh94W7a5=S zR`@ga#r(>o2ZUofpuopny>~FXY zK}Ywc*wfEgAA|hoDTb5kn1qA^A({ulSvyAdhD_?8{q3?u22Cox9zw1X-EZ{Hw&gTA zaG}OyCi_yzji+e#Sh(-zpWmikU@#_S)El6`4Jlj``ILYW7yJV7FG%GFW`QHV0ZK78 zu&4|mtM2zJ`27Tc%r78~4=lL(kM{6;X-iGsyif*x{P}$?R`!Rz5VSykFpgA_(c@eo zkH#1;W+Z^JlV8X#U%3F30s^3_&`QC!L$K){lmm~(+g>M8)MmdX?*F1nOO=XvoIjMV zyRFnnDw zb%e&SK3Qejrj|z~Lws(Tjwg87#O4NvN@4=o5TT~ZhI1^7&?tq7Q)FYEGQhijL1N#h zX&phQM_ZM83`}%)CIG^c?$<;;w$W+!ws!c+I}Xwjkx#g|Gxl}WTC1kW0?Oln$n(5@ zoSB?BfpJeD}_8Q;zCX+G@i zFKuitUmTv|ZLF_jttGZeq_eY$4}f!)IF9K>5gQw8oH})a&wuWeciU~ZKW~qR?#}f0 zr>@lbUlO&_Ie|@qB}oX&D_M^NKAHaexNGq8+VZ8o_+MiCvj-EvTvd99l?ZU);H}hZ zbHs5k<2tUs37~cB>J*ivkAB=8DDJdr*EbM>IO=Wt*;E~!X1)LX*2=}S3c3C3)hX7- z*f>rS0YtFf4t^M-PKl!~rBNkL#-{m4>4%GyCs8FrP1rLuUzJ*p8hoc%~w60PhMK$ zEuVjgk39N$nn8ui+yax+bA(~JPoxag0DJr_X~dv9OSid2bM4Z`=EbwW+gw|IBXIID z9P`_G0c3za%>Cpm{o#-SpczyB&rjxcHx+=jY7`L^Rl$*%0A|S4L(&*IK~ZAOJT-LK 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

Z1tNHyLr~o`TSh(Hl}I|{QJRg)1J!mt7|X8 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 literal 0 HcmV?d00001 diff --git a/spine-sdl/example/main.c b/spine-sdl/example/main.c index 0dcf11ca5..8992a0ef1 100644 --- a/spine-sdl/example/main.c +++ b/spine-sdl/example/main.c @@ -46,18 +46,19 @@ int main() { return -1; } - spAtlas *atlas = spAtlas_createFromFile("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas", renderer); + spAtlas *atlas = spAtlas_createFromFile("data/spineboy.atlas", renderer); spSkeletonJson *json = spSkeletonJson_create(atlas); json->scale = 0.5f; - spSkeletonData *skeletonData = spSkeletonJson_readSkeletonDataFile(json, "/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.json"); + spSkeletonData *skeletonData = spSkeletonJson_readSkeletonDataFile(json, "data/spineboy-pro.json"); spAnimationStateData *animationStateData = spAnimationStateData_create(skeletonData); + animationStateData->defaultMix = 0.2f; spSkeletonDrawable *drawable = spSkeletonDrawable_create(skeletonData, animationStateData); - drawable->skeleton->x = 400; drawable->skeleton->y = 500; spSkeleton_setToSetupPose(drawable->skeleton); spSkeletonDrawable_update(drawable, 0); - spAnimationState_setAnimationByName(drawable->animationState, 0, "run", -1); + spAnimationState_setAnimationByName(drawable->animationState, 0, "portal", 0); + spAnimationState_addAnimationByName(drawable->animationState, 0, "run", -1, 0); int quit = 0; uint64_t lastFrameTime = SDL_GetPerformanceCounter(); diff --git a/spine-sdl/example/main.cpp b/spine-sdl/example/main.cpp index cc1c7e096..50083a6b8 100644 --- a/spine-sdl/example/main.cpp +++ b/spine-sdl/example/main.cpp @@ -47,16 +47,18 @@ int main() { } spine::SDLTextureLoader textureLoader(renderer); - spine::Atlas atlas("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy.atlas", &textureLoader); + spine::Atlas atlas("data/spineboy.atlas", &textureLoader); spine::AtlasAttachmentLoader attachmentLoader(&atlas); spine::SkeletonJson json(&attachmentLoader); json.setScale(0.5f); - spine::SkeletonData *skeletonData = json.readSkeletonDataFile("/Users/badlogic/workspaces/spine-runtimes/examples/spineboy/export/spineboy-pro.json"); + spine::SkeletonData *skeletonData = json.readSkeletonDataFile("data/spineboy-pro.json"); spine::SkeletonDrawable drawable(skeletonData); + drawable.animationState->getData()->setDefaultMix(0.2f); drawable.skeleton->setPosition(400, 500); drawable.skeleton->setToSetupPose(); drawable.update(0); - drawable.animationState->setAnimation(0, "run", true); + drawable.animationState->setAnimation(0, "portal", true); + drawable.animationState->addAnimation(0, "run", true, 0); bool quit = false; uint64_t lastFrameTime = SDL_GetPerformanceCounter(); From 6ef7df4be600729a5da3d687ee3bf0423adfdc35 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Tue, 2 Aug 2022 16:04:54 +0200 Subject: [PATCH 10/18] [unity] Added example component `SkeletonGraphicRenderTexture`. Closes #2130. Added additional render callback delegates at SkeletonGraphic. --- CHANGELOG.md | 2 + .../RenderTexture FadeOut Transparency.unity | 291 +++++++++++++++++- .../Scripts/RenderTextureFadeoutExample.cs | 16 +- .../SkeletonGraphicRenderTexture.cs | 253 +++++++++++++++ .../SkeletonGraphicRenderTexture.cs.meta | 11 + .../SkeletonRenderTexture.cs | 78 +---- .../SkeletonRenderTextureBase.cs | 88 +++++- .../SkeletonRenderTextureBase.cs.meta | 11 + .../spine-unity/Components/SkeletonGraphic.cs | 204 +++++++++--- .../spine-unity/Utility/MathUtilities.cs | 71 +++++ .../spine-unity/Utility/MathUtilities.cs.meta | 11 + 11 files changed, 882 insertions(+), 154 deletions(-) create mode 100644 spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonGraphicRenderTexture.cs create mode 100644 spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonGraphicRenderTexture.cs.meta create mode 100644 spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs.meta create mode 100644 spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MathUtilities.cs create mode 100644 spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MathUtilities.cs.meta diff --git a/CHANGELOG.md b/CHANGELOG.md index 72e4e19d2..223e4d9bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -316,6 +316,8 @@ * Added example component `SkeletonRenderTexture` to render a `SkeletonRenderer` to a `RenderTexture`, mainly for proper transparency. Added an example scene named `RenderTexture FadeOut Transparency` that demonstrates usage for a fadeout transparency effect. * Added another fadeout example component named `SkeletonRenderTextureFadeout` which takes over transparency fadeout when enabled. You can use this component as-is, attach it in disabled state and enable it to start a fadeout effect. * Timeline clips now offer an additional `Alpha` parameter for setting a custom constant mix alpha value other than 1.0, just as `TrackEntry.Alpha`. Defaults to 1.0. + * `SkeletonGraphic` now provides additional render callback delegates `OnInstructionsPrepared`, `AssignMeshOverrideSingleRenderer` and `AssignMeshOverrideMultipleRenderers`. `OnInstructionsPrepared` is raised at the end of LateUpdate after render instructions are done, target renderers are prepared, and the mesh is ready to be generated. The two `AssignMeshOverride` delegates allow separate code to take over mesh and material assignment of a `SkeletonGraphic` component. + * Added example component `SkeletonGraphicRenderTexture` to render a `SkeletonGraphic` to a `RenderTexture` (similar as `SkeletonRenderTexture`), mainly for proper transparency. Extended example scene `RenderTexture FadeOut Transparency` accordingly. * **Changes of default values** diff --git a/spine-unity/Assets/Spine Examples/Other Examples/RenderTexture FadeOut Transparency.unity b/spine-unity/Assets/Spine Examples/Other Examples/RenderTexture FadeOut Transparency.unity index c5ae69f86..93acf07d8 100644 --- a/spine-unity/Assets/Spine Examples/Other Examples/RenderTexture FadeOut Transparency.unity +++ b/spine-unity/Assets/Spine Examples/Other Examples/RenderTexture FadeOut Transparency.unity @@ -153,7 +153,7 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 592567554} - m_RootOrder: 0 + m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 1, y: 1} m_AnchorMax: {x: 1, y: 1} @@ -256,6 +256,8 @@ MonoBehaviour: materialsInsideMask: [] materialsOutsideMask: [] disableRenderingOnOverride: 1 + updateTiming: 1 + unscaledTime: 0 _animationName: run loop: 1 timeScale: 1 @@ -316,11 +318,11 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 334034152} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: -5.93, y: 0, z: 5.66} + m_LocalPosition: {x: -7.83, y: 0, z: 5.66} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 4 + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &541830406 GameObject: @@ -352,7 +354,7 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 592567554} - m_RootOrder: 1 + m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 1, y: 1} m_AnchorMax: {x: 1, y: 1} @@ -433,9 +435,11 @@ RectTransform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0} m_Children: + - {fileID: 1911967440} - {fileID: 71621967} - {fileID: 541830407} - {fileID: 1682675646} + - {fileID: 1735507358} m_Father: {fileID: 1799507978} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -488,6 +492,41 @@ Canvas: m_SortingLayerID: 0 m_SortingOrder: 0 m_TargetDisplay: 0 +--- !u!1 &1089682726 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1089682727} + m_Layer: 0 + m_Name: CustomRenderRect + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1089682727 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1089682726} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1911967440} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 126.27, y: 7.130005} + m_SizeDelta: {x: 1893.1, y: 1654.617} + m_Pivot: {x: 0.5, y: 0.5} --- !u!1 &1368805070 GameObject: m_ObjectHideFlags: 0 @@ -532,6 +571,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: renderTextureFadeout: {fileID: 1786065619} + renderTextureFadeoutCanvas: {fileID: 1911967443} normalSkeletonRenderer: {fileID: 334034153} --- !u!1 &1369381599 GameObject: @@ -805,12 +845,12 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 592567554} - m_RootOrder: 2 + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -331.4, y: -186} - m_SizeDelta: {x: 1176.1, y: -365} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0.6932292, y: 0.18750001} + m_AnchoredPosition: {x: 18.079224, y: 1.5001221} + m_SizeDelta: {x: -44.942017, y: 10} m_Pivot: {x: 0.5, y: 0.5} --- !u!114 &1682675647 MonoBehaviour: @@ -847,12 +887,13 @@ MonoBehaviour: m_LineSpacing: 1 m_Text: 'This scene demonstrates the problems of using conventional alpha transparency for a fadeout effect (left), and how this problem can be fixed by using a RenderTexture - (right). + (center and right). - Spineboy on the right uses a SkeletonRenderTexture component to - render the overlapping mesh to a RenderTexture first and then draw this texture - to the scene at once using a single quad.' + The two Spineboys on the right use SkeletonRenderTexture + and SkeletonGraphicRenderTexture components to render the overlapping mesh to + a RenderTexture first and then draw this texture to the scene at once using a + single quad.' --- !u!222 &1682675648 CanvasRenderer: m_ObjectHideFlags: 0 @@ -861,6 +902,87 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1682675645} m_CullTransparentMesh: 1 +--- !u!1 &1735507357 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1735507358} + - component: {fileID: 1735507360} + - component: {fileID: 1735507359} + m_Layer: 5 + m_Name: SkeletonGraphic Notes + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1735507358 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1735507357} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 592567554} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.6932292, y: 0} + m_AnchorMax: {x: 1, y: 0.18750001} + m_AnchoredPosition: {x: 6.5009766, y: -2.5753784} + m_SizeDelta: {x: -49.462, y: 1.8482} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1735507359 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1735507357} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 26 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 2 + m_MaxSize: 40 + m_Alignment: 2 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 1 + m_LineSpacing: 1 + m_Text: SkeletonGraphicRenderTexture supports setting a RectTransform as 'Custom + Render Rect' to define render texture bounds different from the SkeletonGraphic + rect. +--- !u!222 &1735507360 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1735507357} + m_CullTransparentMesh: 1 --- !u!1 &1786065613 GameObject: m_ObjectHideFlags: 0 @@ -895,11 +1017,11 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: color: {r: 1, g: 1, b: 1, a: 1} - quadMaterial: {fileID: 2100000, guid: 4c507f887c6274a44a603d96e0eabf2a, type: 2} - targetCamera: {fileID: 0} maxRenderTextureSize: 1024 quad: {fileID: 0} renderTexture: {fileID: 0} + targetCamera: {fileID: 0} + quadMaterial: {fileID: 2100000, guid: 4c507f887c6274a44a603d96e0eabf2a, type: 2} --- !u!114 &1786065615 MonoBehaviour: m_ObjectHideFlags: 0 @@ -935,6 +1057,8 @@ MonoBehaviour: materialsInsideMask: [] materialsOutsideMask: [] disableRenderingOnOverride: 1 + updateTiming: 1 + unscaledTime: 0 _animationName: run loop: 1 timeScale: 1 @@ -995,11 +1119,11 @@ Transform: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1786065613} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 3.32, y: 0, z: 5.66} + m_LocalPosition: {x: 2.38, y: 0, z: 5.66} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 3 + m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &1786065619 MonoBehaviour: @@ -1045,3 +1169,136 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1911967439 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1911967440} + - component: {fileID: 1911967442} + - component: {fileID: 1911967441} + - component: {fileID: 1911967444} + - component: {fileID: 1911967443} + m_Layer: 0 + m_Name: SkeletonGraphic (spineboy-pro) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1911967440 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1911967439} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0.3, y: 0.3, z: 0.3} + m_Children: + - {fileID: 1089682727} + m_Father: {fileID: 592567554} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: 84, y: -317.51} + m_SizeDelta: {x: 1185.6, y: 1302.059} + m_Pivot: {x: 0.63858336, y: 0.010301443} +--- !u!114 &1911967441 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1911967439} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d85b887af7e6c3f45a2e2d2920d641bc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 2100000, guid: b66cf7a186d13054989b33a5c90044e4, type: 2} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + skeletonDataAsset: {fileID: 11400000, guid: af38a3de26ed9b84abc2fe7c7f3b209d, type: 2} + additiveMaterial: {fileID: 2100000, guid: 2e8245019faeb8c43b75f9ca3ac8ee34, type: 2} + multiplyMaterial: {fileID: 2100000, guid: e74a1f8978a7da348a721508d0d58834, type: 2} + screenMaterial: {fileID: 2100000, guid: bab24c479f34eec45be6ea8595891569, type: 2} + initialSkinName: default + initialFlipX: 0 + initialFlipY: 0 + startingAnimation: run + startingLoop: 1 + timeScale: 1 + freeze: 0 + updateWhenInvisible: 3 + allowMultipleCanvasRenderers: 0 + canvasRenderers: [] + separatorSlotNames: [] + enableSeparatorSlots: 0 + separatorParts: [] + updateSeparatorPartLocation: 1 + disableMeshAssignmentOnOverride: 1 + meshGenerator: + settings: + useClipping: 1 + zSpacing: 0 + pmaVertexColors: 1 + tintBlack: 0 + canvasGroupTintBlack: 0 + calculateTangents: 0 + addNormals: 0 + immutableTriangles: 0 + updateTiming: 1 + unscaledTime: 0 +--- !u!222 &1911967442 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1911967439} + m_CullTransparentMesh: 0 +--- !u!114 &1911967443 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1911967439} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5fc94f89310427643babb41e000a8462, type: 3} + m_Name: + m_EditorClassIdentifier: + fadeoutSeconds: 2 +--- !u!114 &1911967444 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1911967439} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6cbe1f11426513d49ad8e21e9d6643f7, type: 3} + m_Name: + m_EditorClassIdentifier: + color: {r: 1, g: 1, b: 1, a: 1} + maxRenderTextureSize: 1024 + quad: {fileID: 0} + renderTexture: {fileID: 0} + targetCamera: {fileID: 0} + customRenderRect: {fileID: 1089682727} + meshRendererMaterialForTexture: + - texture: {fileID: 2800000, guid: 4ea2c33e839afb34c98f66e892b3b2d2, type: 3} + material: {fileID: 2100000, guid: f89bbf05902e77242a3ad20f3c927353, type: 2} diff --git a/spine-unity/Assets/Spine Examples/Scripts/RenderTextureFadeoutExample.cs b/spine-unity/Assets/Spine Examples/Scripts/RenderTextureFadeoutExample.cs index 19b67376d..4e5babaf2 100644 --- a/spine-unity/Assets/Spine Examples/Scripts/RenderTextureFadeoutExample.cs +++ b/spine-unity/Assets/Spine Examples/Scripts/RenderTextureFadeoutExample.cs @@ -39,6 +39,7 @@ namespace Spine.Unity.Examples { public class RenderTextureFadeoutExample : MonoBehaviour { public SkeletonRenderTextureFadeout renderTextureFadeout; + public SkeletonRenderTextureFadeout renderTextureFadeoutCanvas; public SkeletonRenderer normalSkeletonRenderer; float fadeoutSeconds = 2.0f; @@ -47,8 +48,9 @@ namespace Spine.Unity.Examples { IEnumerator Start () { while (true) { StartFadeoutBad(); - StartFadeoutGood(); - yield return new WaitForSeconds(5.0f); + StartFadeoutGood(renderTextureFadeout); + StartFadeoutGood(renderTextureFadeoutCanvas); + yield return new WaitForSeconds(fadeoutSeconds + 1.0f); } } void Update () { @@ -75,12 +77,12 @@ namespace Spine.Unity.Examples { fadeoutSecondsRemaining = fadeoutSeconds; } - void StartFadeoutGood () { - renderTextureFadeout.gameObject.SetActive(true); + void StartFadeoutGood (SkeletonRenderTextureFadeout fadeoutComponent) { + fadeoutComponent.gameObject.SetActive(true); // enabling the SkeletonRenderTextureFadeout component starts the fadeout. - renderTextureFadeout.enabled = true; - renderTextureFadeout.OnFadeoutComplete -= DisableGameObject; - renderTextureFadeout.OnFadeoutComplete += DisableGameObject; + fadeoutComponent.enabled = true; + fadeoutComponent.OnFadeoutComplete -= DisableGameObject; + fadeoutComponent.OnFadeoutComplete += DisableGameObject; } void DisableGameObject (SkeletonRenderTextureFadeout target) { diff --git a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonGraphicRenderTexture.cs b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonGraphicRenderTexture.cs new file mode 100644 index 000000000..af30ad8e7 --- /dev/null +++ b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonGraphicRenderTexture.cs @@ -0,0 +1,253 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated January 1, 2020. Replaces all prior versions. + * + * Copyright (c) 2013-2022, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, + * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.UI; + +namespace Spine.Unity.Examples { + + /// + /// When enabled, this component renders a skeleton to a RenderTexture and + /// then draws this RenderTexture at a UI RawImage quad of the same size. + /// This allows changing transparency at a single quad, which produces a more + /// natural fadeout effect. + /// Note: It is recommended to keep this component disabled as much as possible + /// because of the additional rendering overhead. Only enable it when alpha blending is required. + /// + [RequireComponent(typeof(SkeletonGraphic))] + public class SkeletonGraphicRenderTexture : SkeletonRenderTextureBase { + [System.Serializable] + public struct TextureMaterialPair { + public Texture texture; + public Material material; + + public TextureMaterialPair (Texture texture, Material material) { + this.texture = texture; + this.material = material; + } + } + + public RectTransform customRenderRect; + protected SkeletonGraphic skeletonGraphic; + public List meshRendererMaterialForTexture = new List(); + protected CanvasRenderer quadCanvasRenderer; + protected RawImage quadRawImage; + protected readonly Vector3[] worldCorners = new Vector3[4]; + + protected override void Awake () { + base.Awake(); + skeletonGraphic = this.GetComponent(); + if (targetCamera == null) { + targetCamera = skeletonGraphic.canvas.worldCamera; + if (targetCamera == null) + targetCamera = Camera.main; + } + CreateQuadChild(); + } + + void CreateQuadChild () { + quad = new GameObject(this.name + " RenderTexture", typeof(CanvasRenderer), typeof(RawImage)); + quad.transform.SetParent(this.transform.parent, false); + quadCanvasRenderer = quad.GetComponent(); + quadRawImage = quad.GetComponent(); + + quadMesh = new Mesh(); + quadMesh.MarkDynamic(); + quadMesh.name = "RenderTexture Quad"; + quadMesh.hideFlags = HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor; + } + + void Reset () { + skeletonGraphic = this.GetComponent(); + AtlasAssetBase[] atlasAssets = skeletonGraphic.SkeletonDataAsset.atlasAssets; + for (int i = 0; i < atlasAssets.Length; ++i) { + foreach (var material in atlasAssets[i].Materials) { + if (material.mainTexture != null) { + meshRendererMaterialForTexture.Add( + new TextureMaterialPair(material.mainTexture, material)); + } + } + } + } + + void OnEnable () { + skeletonGraphic.OnInstructionsPrepared += PrepareQuad; + skeletonGraphic.AssignMeshOverrideSingleRenderer += RenderSingleMeshToRenderTexture; + skeletonGraphic.AssignMeshOverrideMultipleRenderers += RenderMultipleMeshesToRenderTexture; + skeletonGraphic.disableMeshAssignmentOnOverride = true; + skeletonGraphic.OnMeshAndMaterialsUpdated += RenderOntoQuad; + var canvasRenderers = skeletonGraphic.canvasRenderers; + for (int i = 0; i < canvasRenderers.Count; ++i) + canvasRenderers[i].cull = true; + + if (quadCanvasRenderer) + quadCanvasRenderer.gameObject.SetActive(true); + } + + void OnDisable () { + skeletonGraphic.OnInstructionsPrepared -= PrepareQuad; + skeletonGraphic.AssignMeshOverrideSingleRenderer -= RenderSingleMeshToRenderTexture; + skeletonGraphic.AssignMeshOverrideMultipleRenderers -= RenderMultipleMeshesToRenderTexture; + skeletonGraphic.disableMeshAssignmentOnOverride = false; + skeletonGraphic.OnMeshAndMaterialsUpdated -= RenderOntoQuad; + var canvasRenderers = skeletonGraphic.canvasRenderers; + for (int i = 0; i < canvasRenderers.Count; ++i) + canvasRenderers[i].cull = false; + + if (quadCanvasRenderer) + quadCanvasRenderer.gameObject.SetActive(false); + if (renderTexture) + RenderTexture.ReleaseTemporary(renderTexture); + allocatedRenderTextureSize = Vector2Int.zero; + } + + void PrepareQuad (SkeletonRendererInstruction instruction) { + PrepareForMesh(); + SetupQuad(); + } + + void RenderOntoQuad (SkeletonGraphic skeletonRenderer) { + AssignAtQuad(); + } + + protected void PrepareForMesh () { + // We need to get the min/max of all four corners, rotation of the skeleton + // in combination with perspective projection otherwise might lead to incorrect + // screen space min/max. + RectTransform rectTransform = customRenderRect ? customRenderRect : skeletonGraphic.rectTransform; + rectTransform.GetWorldCorners(worldCorners); + + RenderMode canvasRenderMode = skeletonGraphic.canvas.renderMode; + Vector3 screenCorner0, screenCorner1, screenCorner2, screenCorner3; + // note: world corners are ordered bottom left, top left, top right, bottom right. + // This corresponds to 0, 3, 1, 2 in our desired order. + if (canvasRenderMode == RenderMode.ScreenSpaceOverlay) { + screenCorner0 = worldCorners[0]; + screenCorner1 = worldCorners[3]; + screenCorner2 = worldCorners[1]; + screenCorner3 = worldCorners[2]; + } else { + screenCorner0 = targetCamera.WorldToScreenPoint(worldCorners[0]); + screenCorner1 = targetCamera.WorldToScreenPoint(worldCorners[3]); + screenCorner2 = targetCamera.WorldToScreenPoint(worldCorners[1]); + screenCorner3 = targetCamera.WorldToScreenPoint(worldCorners[2]); + } + + // To avoid perspective distortion when rotated, we project all vertices + // onto a plane parallel to the view frustum near plane. + // Avoids the requirement of 'noperspective' vertex attribute interpolation modifier in shaders. + float averageScreenDepth = (screenCorner0.z + screenCorner1.z + screenCorner2.z + screenCorner3.z) / 4.0f; + screenCorner0.z = screenCorner1.z = screenCorner2.z = screenCorner3.z = averageScreenDepth; + + if (canvasRenderMode == RenderMode.ScreenSpaceOverlay) { + worldCornerNoDistortion0 = screenCorner0; + worldCornerNoDistortion1 = screenCorner1; + worldCornerNoDistortion2 = screenCorner2; + worldCornerNoDistortion3 = screenCorner3; + } else { + worldCornerNoDistortion0 = targetCamera.ScreenToWorldPoint(screenCorner0); + worldCornerNoDistortion1 = targetCamera.ScreenToWorldPoint(screenCorner1); + worldCornerNoDistortion2 = targetCamera.ScreenToWorldPoint(screenCorner2); + worldCornerNoDistortion3 = targetCamera.ScreenToWorldPoint(screenCorner3); + } + Vector3 screenSpaceMin, screenSpaceMax; + PrepareTextureMapping(out screenSpaceMin, out screenSpaceMax, + screenCorner0, screenCorner1, screenCorner2, screenCorner3); + PrepareCommandBuffer(targetCamera, screenSpaceMin, screenSpaceMax); + } + + protected Material MeshRendererMaterialForTexture (Texture texture) { + return meshRendererMaterialForTexture.Find(x => x.texture == texture).material; + } + + protected void RenderSingleMeshToRenderTexture (Mesh mesh, Material graphicMaterial, Texture texture) { + Material meshRendererMaterial = MeshRendererMaterialForTexture(texture); + commandBuffer.DrawMesh(mesh, transform.localToWorldMatrix, meshRendererMaterial, 0, -1); + Graphics.ExecuteCommandBuffer(commandBuffer); + } + + protected void RenderMultipleMeshesToRenderTexture (int meshCount, + Mesh[] meshes, Material[] graphicMaterials, Texture[] textures) { + + for (int i = 0; i < meshCount; ++i) { + Material meshRendererMaterial = MeshRendererMaterialForTexture(textures[i]); + commandBuffer.DrawMesh(meshes[i], transform.localToWorldMatrix, meshRendererMaterial, 0, -1); + } + Graphics.ExecuteCommandBuffer(commandBuffer); + } + + protected void SetupQuad () { + quadRawImage.texture = this.renderTexture; + quadRawImage.color = color; + quadCanvasRenderer.SetColor(color); + + var srcRectTransform = skeletonGraphic.rectTransform; + var dstRectTransform = quadRawImage.rectTransform; + + dstRectTransform.anchorMin = srcRectTransform.anchorMin; + dstRectTransform.anchorMax = srcRectTransform.anchorMax; + dstRectTransform.anchoredPosition = srcRectTransform.anchoredPosition; + dstRectTransform.pivot = srcRectTransform.pivot; + dstRectTransform.localScale = srcRectTransform.localScale; + dstRectTransform.sizeDelta = srcRectTransform.sizeDelta; + dstRectTransform.rotation = srcRectTransform.rotation; + } + + protected void PrepareCommandBuffer (Camera targetCamera, Vector3 screenSpaceMin, Vector3 screenSpaceMax) { + commandBuffer.Clear(); + commandBuffer.SetRenderTarget(renderTexture); + commandBuffer.ClearRenderTarget(true, true, Color.clear); + + Rect canvasRect = skeletonGraphic.canvas.pixelRect; + + Matrix4x4 projectionMatrix = Matrix4x4.Ortho( + canvasRect.x, canvasRect.x + canvasRect.width, + canvasRect.y, canvasRect.y + canvasRect.height, + float.MinValue, float.MaxValue); + + RenderMode canvasRenderMode = skeletonGraphic.canvas.renderMode; + if (canvasRenderMode == RenderMode.ScreenSpaceOverlay) { + commandBuffer.SetViewMatrix(Matrix4x4.identity); + commandBuffer.SetProjectionMatrix(projectionMatrix); + } else { + commandBuffer.SetViewMatrix(targetCamera.worldToCameraMatrix); + commandBuffer.SetProjectionMatrix(targetCamera.projectionMatrix); + } + + Vector2 targetCameraViewportSize = targetCamera.pixelRect.size; + commandBuffer.SetViewport(new Rect(-screenSpaceMin, targetCameraViewportSize)); + } + + protected override void AssignMeshAtRenderer () { + quadCanvasRenderer.SetMesh(quadMesh); + } + } +} diff --git a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonGraphicRenderTexture.cs.meta b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonGraphicRenderTexture.cs.meta new file mode 100644 index 000000000..7d3e6d8ed --- /dev/null +++ b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonGraphicRenderTexture.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6cbe1f11426513d49ad8e21e9d6643f7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTexture.cs b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTexture.cs index 19c814c04..4ded9af01 100644 --- a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTexture.cs +++ b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTexture.cs @@ -35,7 +35,6 @@ #define HAS_GET_SHARED_MATERIALS #endif -using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; @@ -54,20 +53,11 @@ namespace Spine.Unity.Examples { public class SkeletonRenderTexture : SkeletonRenderTextureBase { #if HAS_GET_SHARED_MATERIALS public Material quadMaterial; - public Camera targetCamera; protected SkeletonRenderer skeletonRenderer; protected MeshRenderer meshRenderer; protected MeshFilter meshFilter; protected MeshRenderer quadMeshRenderer; protected MeshFilter quadMeshFilter; - protected Vector3 worldCornerNoDistortion0; - protected Vector3 worldCornerNoDistortion1; - protected Vector3 worldCornerNoDistortion2; - protected Vector3 worldCornerNoDistortion3; - protected Vector2 uvCorner0; - protected Vector2 uvCorner1; - protected Vector2 uvCorner2; - protected Vector2 uvCorner3; private MaterialPropertyBlock propertyBlock; private readonly List materials = new List(); @@ -160,40 +150,13 @@ namespace Spine.Unity.Examples { worldCornerNoDistortion2 = targetCamera.ScreenToWorldPoint(screenCorner2); worldCornerNoDistortion3 = targetCamera.ScreenToWorldPoint(screenCorner3); - Vector3 screenSpaceMin = - Vector3.Min(screenCorner0, Vector3.Min(screenCorner1, - Vector3.Min(screenCorner2, screenCorner3))); - Vector3 screenSpaceMax = - Vector3.Max(screenCorner0, Vector3.Max(screenCorner1, - Vector3.Max(screenCorner2, screenCorner3))); - // ensure we are on whole pixel borders - screenSpaceMin.x = Mathf.Floor(screenSpaceMin.x); - screenSpaceMin.y = Mathf.Floor(screenSpaceMin.y); - screenSpaceMax.x = Mathf.Ceil(screenSpaceMax.x); - screenSpaceMax.y = Mathf.Ceil(screenSpaceMax.y); - - // inverse-map screenCornerN to screenSpaceMin/screenSpaceMax area to get UV coordinates - uvCorner0 = InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner0); - uvCorner1 = InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner1); - uvCorner2 = InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner2); - uvCorner3 = InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner3); - - requiredRenderTextureSize = new Vector2Int( - Math.Min(maxRenderTextureSize, Math.Abs((int)screenSpaceMax.x - (int)screenSpaceMin.x)), - Math.Min(maxRenderTextureSize, Math.Abs((int)screenSpaceMax.y - (int)screenSpaceMin.y))); - - PrepareRenderTexture(); + Vector3 screenSpaceMin, screenSpaceMax; + PrepareTextureMapping(out screenSpaceMin, out screenSpaceMax, + screenCorner0, screenCorner1, screenCorner2, screenCorner3); PrepareCommandBuffer(targetCamera, screenSpaceMin, screenSpaceMax); } - protected Vector2 InverseLerp (Vector2 a, Vector2 b, Vector2 value) { - return new Vector2( - (value.x - a.x) / (b.x - a.x), - (value.y - a.y) / (b.y - a.y)); - } - protected void PrepareCommandBuffer (Camera targetCamera, Vector3 screenSpaceMin, Vector3 screenSpaceMax) { - commandBuffer.Clear(); commandBuffer.SetRenderTarget(renderTexture); commandBuffer.ClearRenderTarget(true, true, Color.clear); @@ -214,40 +177,7 @@ namespace Spine.Unity.Examples { Graphics.ExecuteCommandBuffer(commandBuffer); } - protected void AssignAtQuad () { - Transform quadTransform = quadMeshRenderer.transform; - quadTransform.position = this.transform.position; - quadTransform.rotation = this.transform.rotation; - quadTransform.localScale = this.transform.localScale; - - Vector3 v0 = quadTransform.InverseTransformPoint(worldCornerNoDistortion0); - Vector3 v1 = quadTransform.InverseTransformPoint(worldCornerNoDistortion1); - Vector3 v2 = quadTransform.InverseTransformPoint(worldCornerNoDistortion2); - Vector3 v3 = quadTransform.InverseTransformPoint(worldCornerNoDistortion3); - Vector3[] vertices = new Vector3[4] { v0, v1, v2, v3 }; - - quadMesh.vertices = vertices; - - int[] indices = new int[6] { 0, 2, 1, 2, 3, 1 }; - quadMesh.triangles = indices; - - Vector3[] normals = new Vector3[4] { - -Vector3.forward, - -Vector3.forward, - -Vector3.forward, - -Vector3.forward - }; - quadMesh.normals = normals; - - float maxU = (float)requiredRenderTextureSize.x / (float)allocatedRenderTextureSize.x; - float maxV = (float)requiredRenderTextureSize.y / (float)allocatedRenderTextureSize.y; - Vector2[] uv = new Vector2[4] { - new Vector2(uvCorner0.x * maxU, uvCorner0.y * maxV), - new Vector2(uvCorner1.x * maxU, uvCorner1.y * maxV), - new Vector2(uvCorner2.x * maxU, uvCorner2.y * maxV), - new Vector2(uvCorner3.x * maxU, uvCorner3.y * maxV), - }; - quadMesh.uv = uv; + protected override void AssignMeshAtRenderer () { quadMeshFilter.mesh = quadMesh; quadMeshRenderer.sharedMaterial.mainTexture = this.renderTexture; quadMeshRenderer.sharedMaterial.color = color; diff --git a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs index 9a6e13959..8842385ff 100644 --- a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs +++ b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs @@ -27,31 +27,33 @@ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -#if UNITY_2019_3_OR_NEWER -#define HAS_FORCE_RENDER_OFF -#endif - -#if UNITY_2018_2_OR_NEWER -#define HAS_GET_SHARED_MATERIALS -#endif - +using System; using UnityEngine; using UnityEngine.Rendering; namespace Spine.Unity.Examples { public abstract class SkeletonRenderTextureBase : MonoBehaviour { -#if HAS_GET_SHARED_MATERIALS public Color color = Color.white; public int maxRenderTextureSize = 1024; public GameObject quad; protected Mesh quadMesh; public RenderTexture renderTexture; + public Camera targetCamera; protected CommandBuffer commandBuffer; protected Vector2Int requiredRenderTextureSize; protected Vector2Int allocatedRenderTextureSize; + protected Vector3 worldCornerNoDistortion0; + protected Vector3 worldCornerNoDistortion1; + protected Vector3 worldCornerNoDistortion2; + protected Vector3 worldCornerNoDistortion3; + protected Vector2 uvCorner0; + protected Vector2 uvCorner1; + protected Vector2 uvCorner2; + protected Vector2 uvCorner3; + protected virtual void Awake () { commandBuffer = new CommandBuffer(); } @@ -61,6 +63,34 @@ namespace Spine.Unity.Examples { RenderTexture.ReleaseTemporary(renderTexture); } + protected void PrepareTextureMapping (out Vector3 screenSpaceMin, out Vector3 screenSpaceMax, + Vector3 screenCorner0, Vector3 screenCorner1, Vector3 screenCorner2, Vector3 screenCorner3) { + + screenSpaceMin = + Vector3.Min(screenCorner0, Vector3.Min(screenCorner1, + Vector3.Min(screenCorner2, screenCorner3))); + screenSpaceMax = + Vector3.Max(screenCorner0, Vector3.Max(screenCorner1, + Vector3.Max(screenCorner2, screenCorner3))); + // ensure we are on whole pixel borders + screenSpaceMin.x = Mathf.Floor(screenSpaceMin.x); + screenSpaceMin.y = Mathf.Floor(screenSpaceMin.y); + screenSpaceMax.x = Mathf.Ceil(screenSpaceMax.x); + screenSpaceMax.y = Mathf.Ceil(screenSpaceMax.y); + + // inverse-map screenCornerN to screenSpaceMin/screenSpaceMax area to get UV coordinates + uvCorner0 = MathUtilities.InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner0); + uvCorner1 = MathUtilities.InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner1); + uvCorner2 = MathUtilities.InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner2); + uvCorner3 = MathUtilities.InverseLerp(screenSpaceMin, screenSpaceMax, screenCorner3); + + requiredRenderTextureSize = new Vector2Int( + Math.Min(maxRenderTextureSize, Math.Abs((int)screenSpaceMax.x - (int)screenSpaceMin.x)), + Math.Min(maxRenderTextureSize, Math.Abs((int)screenSpaceMax.y - (int)screenSpaceMin.y))); + + PrepareRenderTexture(); + } + protected void PrepareRenderTexture () { Vector2Int textureSize = new Vector2Int( Mathf.NextPowerOfTwo(requiredRenderTextureSize.x), @@ -74,6 +104,44 @@ namespace Spine.Unity.Examples { allocatedRenderTextureSize = textureSize; } } -#endif + + protected void AssignAtQuad () { + Transform quadTransform = quad.transform; + quadTransform.position = this.transform.position; + quadTransform.rotation = this.transform.rotation; + quadTransform.localScale = this.transform.localScale; + + Vector3 v0 = quadTransform.InverseTransformPoint(worldCornerNoDistortion0); + Vector3 v1 = quadTransform.InverseTransformPoint(worldCornerNoDistortion1); + Vector3 v2 = quadTransform.InverseTransformPoint(worldCornerNoDistortion2); + Vector3 v3 = quadTransform.InverseTransformPoint(worldCornerNoDistortion3); + Vector3[] vertices = new Vector3[4] { v0, v1, v2, v3 }; + + quadMesh.vertices = vertices; + + int[] indices = new int[6] { 0, 1, 2, 2, 1, 3 }; + quadMesh.triangles = indices; + + Vector3[] normals = new Vector3[4] { + -Vector3.forward, + -Vector3.forward, + -Vector3.forward, + -Vector3.forward + }; + quadMesh.normals = normals; + + float maxU = (float)requiredRenderTextureSize.x / (float)allocatedRenderTextureSize.x; + float maxV = (float)requiredRenderTextureSize.y / (float)allocatedRenderTextureSize.y; + Vector2[] uv = new Vector2[4] { + new Vector2(uvCorner0.x * maxU, uvCorner0.y * maxV), + new Vector2(uvCorner1.x * maxU, uvCorner1.y * maxV), + new Vector2(uvCorner2.x * maxU, uvCorner2.y * maxV), + new Vector2(uvCorner3.x * maxU, uvCorner3.y * maxV), + }; + quadMesh.uv = uv; + AssignMeshAtRenderer(); + } + + protected abstract void AssignMeshAtRenderer (); } } diff --git a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs.meta b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs.meta new file mode 100644 index 000000000..e63c652b4 --- /dev/null +++ b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ccf9b5e5034b0ea45962f9cf32168dd9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs index fdadf6a72..01f9aa995 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs @@ -183,6 +183,59 @@ namespace Spine.Unity { #endregion #region Overrides + // API for taking over rendering. + /// When true, no meshes and materials are assigned at CanvasRenderers if the used override + /// AssignMeshOverrideSingleRenderer or AssignMeshOverrideMultipleRenderers is non-null. + public bool disableMeshAssignmentOnOverride = true; + /// Delegate type for overriding mesh and material assignment, + /// used when allowMultipleCanvasRenderers is false. + /// Mesh normally assigned at the main CanvasRenderer. + /// Material normally assigned at the main CanvasRenderer. + /// Texture normally assigned at the main CanvasRenderer. + public delegate void MeshAssignmentDelegateSingle (Mesh mesh, Material graphicMaterial, Texture texture); + /// Number of meshes. Don't use meshes.Length as this might be higher + /// due to pre-allocated entries. + /// Mesh array where each element is normally assigned to one of the canvasRenderers. + /// Material array where each element is normally assigned to one of the canvasRenderers. + /// Texture array where each element is normally assigned to one of the canvasRenderers. + public delegate void MeshAssignmentDelegateMultiple (int meshCount, Mesh[] meshes, Material[] graphicMaterials, Texture[] textures); + event MeshAssignmentDelegateSingle assignMeshOverrideSingle; + event MeshAssignmentDelegateMultiple assignMeshOverrideMultiple; + + /// Allows separate code to take over mesh and material assignment for this SkeletonGraphic component. + /// Used when allowMultipleCanvasRenderers is false. + public event MeshAssignmentDelegateSingle AssignMeshOverrideSingleRenderer { + add { + assignMeshOverrideSingle += value; + if (disableMeshAssignmentOnOverride && assignMeshOverrideSingle != null) { + Initialize(false); + } + } + remove { + assignMeshOverrideSingle -= value; + if (disableMeshAssignmentOnOverride && assignMeshOverrideSingle == null) { + Initialize(false); + } + } + } + /// Allows separate code to take over mesh and material assignment for this SkeletonGraphic component. + /// Used when allowMultipleCanvasRenderers is true. + public event MeshAssignmentDelegateMultiple AssignMeshOverrideMultipleRenderers { + add { + assignMeshOverrideMultiple += value; + if (disableMeshAssignmentOnOverride && assignMeshOverrideMultiple != null) { + Initialize(false); + } + } + remove { + assignMeshOverrideMultiple -= value; + if (disableMeshAssignmentOnOverride && assignMeshOverrideMultiple == null) { + Initialize(false); + } + } + } + + [System.NonSerialized] readonly Dictionary customTextureOverride = new Dictionary(); /// Use this Dictionary to override a Texture with a different Texture. public Dictionary CustomTextureOverride { get { return customTextureOverride; } } @@ -334,6 +387,8 @@ namespace Spine.Unity { if (updateMode != UpdateMode.FullUpdate) return; PrepareInstructionsAndRenderers(); + if (OnInstructionsPrepared != null) + OnInstructionsPrepared(this.currentInstructions); SetVerticesDirty(); // triggers Rebuild and avoids potential double-update in a single frame } @@ -390,13 +445,21 @@ namespace Spine.Unity { public bool IsValid { get { return skeleton != null; } } public delegate void SkeletonRendererDelegate (SkeletonGraphic skeletonGraphic); + public delegate void InstructionDelegate (SkeletonRendererInstruction instruction); /// OnRebuild is raised after the Skeleton is successfully initialized. public event SkeletonRendererDelegate OnRebuild; - /// OnMeshAndMaterialsUpdated is at the end of LateUpdate after the Mesh and - /// all materials have been updated. + /// OnInstructionsPrepared is raised at the end of LateUpdate after render instructions + /// are done, target renderers are prepared, and the mesh is ready to be generated. + public event InstructionDelegate OnInstructionsPrepared; + + /// OnMeshAndMaterialsUpdated is raised at the end of Rebuild after the Mesh and + /// all materials have been updated. Note that some Unity API calls are not permitted to be issued from + /// Rebuild, so you may want to subscribe to instead + /// from where you can issue such preparation calls. public event SkeletonRendererDelegate OnMeshAndMaterialsUpdated; + //FIXXME: perform some final checks on documentation and changes, then finalize these changes and commit. then integrate in 4.0_cleanup project as well. protected Spine.AnimationState state; public Spine.AnimationState AnimationState { @@ -411,6 +474,12 @@ namespace Spine.Unity { DoubleBuffered meshBuffers; SkeletonRendererInstruction currentInstructions = new SkeletonRendererInstruction(); readonly ExposedList meshes = new ExposedList(); + readonly ExposedList usedMaterials = new ExposedList(); + readonly ExposedList usedTextures = new ExposedList(); + + public ExposedList MeshesMultipleCanvasRenderers { get { return meshes; } } + public ExposedList MaterialsMultipleCanvasRenderers { get { return usedMaterials; } } + public ExposedList TexturesMultipleCanvasRenderers { get { return usedTextures; } } public Mesh GetLastMesh () { return meshBuffers.GetCurrent().mesh; @@ -513,6 +582,8 @@ namespace Spine.Unity { for (int i = 0; i < canvasRenderers.Count; ++i) canvasRenderers[i].Clear(); DestroyMeshes(); + usedMaterials.Clear(); + usedTextures.Clear(); DisposeMeshBuffers(); } @@ -601,6 +672,7 @@ namespace Spine.Unity { int submeshCount = currentInstructions.submeshInstructions.Count; EnsureCanvasRendererCount(submeshCount); EnsureMeshesCount(submeshCount); + EnsureUsedTexturesAndMaterialsCount(submeshCount); EnsureSeparatorPartCount(); PrepareRendererGameObjects(currentInstructions); } @@ -618,6 +690,7 @@ namespace Spine.Unity { if (!this.allowMultipleCanvasRenderers) { UpdateMeshSingleCanvasRenderer(currentInstructions); } else { + UpdateMaterialsMultipleCanvasRenderers(currentInstructions); UpdateMeshMultipleCanvasRenderers(currentInstructions); } @@ -675,19 +748,67 @@ namespace Spine.Unity { if (updateTriangles) meshGenerator.FillTriangles(mesh); meshGenerator.FillLateVertexData(mesh); - canvasRenderer.SetMesh(mesh); smartMesh.instructionUsed.Set(currentInstructions); + if (assignMeshOverrideSingle != null) + assignMeshOverrideSingle(mesh, this.canvasRenderer.GetMaterial(), this.mainTexture); + + bool assignAtCanvasRenderer = (assignMeshOverrideSingle == null || !disableMeshAssignmentOnOverride); + if (assignAtCanvasRenderer) + canvasRenderer.SetMesh(mesh); + else + canvasRenderer.SetMesh(null); if (currentInstructions.submeshInstructions.Count > 0) { var material = currentInstructions.submeshInstructions.Items[0].material; if (material != null && baseTexture != material.mainTexture) { baseTexture = material.mainTexture; - if (overrideTexture == null) + if (overrideTexture == null && assignAtCanvasRenderer) canvasRenderer.SetTexture(this.mainTexture); } } + } - //this.UpdateMaterial(); // note: This would allocate memory. + protected void UpdateMaterialsMultipleCanvasRenderers (SkeletonRendererInstruction currentInstructions) { + int submeshCount = currentInstructions.submeshInstructions.Count; + bool useOriginalTextureAndMaterial = (customMaterialOverride.Count == 0 && customTextureOverride.Count == 0); + + BlendModeMaterials blendModeMaterials = skeletonDataAsset.blendModeMaterials; + bool hasBlendModeMaterials = blendModeMaterials.RequiresBlendModeMaterials; + + bool pmaVertexColors = meshGenerator.settings.pmaVertexColors; + Material[] usedMaterialItems = usedMaterials.Items; + Texture[] usedTextureItems = usedTextures.Items; + for (int i = 0; i < submeshCount; i++) { + var submeshInstructionItem = currentInstructions.submeshInstructions.Items[i]; + var submeshMaterial = submeshInstructionItem.material; + if (useOriginalTextureAndMaterial) { + usedTextureItems[i] = submeshMaterial.mainTexture; + if (!hasBlendModeMaterials) { + usedMaterialItems[i] = this.materialForRendering; + } else { + BlendMode blendMode = blendModeMaterials.BlendModeForMaterial(submeshMaterial); + Material usedMaterial = this.materialForRendering; + if (blendMode == BlendMode.Additive && !pmaVertexColors && additiveMaterial) { + usedMaterial = additiveMaterial; + } else if (blendMode == BlendMode.Multiply && multiplyMaterial) + usedMaterial = multiplyMaterial; + else if (blendMode == BlendMode.Screen && screenMaterial) + usedMaterial = screenMaterial; + usedMaterialItems[i] = submeshGraphics[i].GetModifiedMaterial(usedMaterial); + } + } else { + var originalTexture = submeshMaterial.mainTexture; + Material usedMaterial; + Texture usedTexture; + if (!customMaterialOverride.TryGetValue(originalTexture, out usedMaterial)) + usedMaterial = material; + if (!customTextureOverride.TryGetValue(originalTexture, out usedTexture)) + usedTexture = originalTexture; + + usedMaterialItems[i] = submeshGraphics[i].GetModifiedMaterial(usedMaterial); + usedTextureItems[i] = usedTexture; + } + } } protected void UpdateMeshMultipleCanvasRenderers (SkeletonRendererInstruction currentInstructions) { @@ -705,6 +826,9 @@ namespace Spine.Unity { bool mainCullTransparentMesh = this.canvasRenderer.cullTransparentMesh; #endif bool pmaVertexColors = meshGenerator.settings.pmaVertexColors; + Material[] usedMaterialItems = usedMaterials.Items; + Texture[] usedTextureItems = usedTextures.Items; + bool assignAtCanvasRenderer = (assignMeshOverrideSingle == null || !disableMeshAssignmentOnOverride); for (int i = 0; i < submeshCount; i++) { var submeshInstructionItem = currentInstructions.submeshInstructions.Items[i]; meshGenerator.Begin(); @@ -717,53 +841,31 @@ namespace Spine.Unity { meshGenerator.FillTriangles(targetMesh); meshGenerator.FillLateVertexData(targetMesh); - var submeshMaterial = submeshInstructionItem.material; var canvasRenderer = canvasRenderers[i]; - canvasRenderer.SetMesh(targetMesh); - canvasRenderer.materialCount = 1; + if (assignMeshOverrideSingle == null || !disableMeshAssignmentOnOverride) + canvasRenderer.SetMesh(targetMesh); + else + canvasRenderer.SetMesh(null); SkeletonSubmeshGraphic submeshGraphic = submeshGraphics[i]; - if (useOriginalTextureAndMaterial) { - Texture usedTexture = submeshMaterial.mainTexture; - if (!hasBlendModeMaterials) - canvasRenderer.SetMaterial(this.materialForRendering, usedTexture); - else { - bool allowCullTransparentMesh = true; - BlendMode blendMode = blendModeMaterials.BlendModeForMaterial(submeshMaterial); - Material usedMaterial = this.materialForRendering; - if (blendMode == BlendMode.Normal) { - if (submeshInstructionItem.hasPMAAdditiveSlot) - allowCullTransparentMesh = false; - } else if (blendMode == BlendMode.Additive) { - if (pmaVertexColors) - allowCullTransparentMesh = false; - else if (additiveMaterial) - usedMaterial = additiveMaterial; - } else if (blendMode == BlendMode.Multiply && multiplyMaterial) - usedMaterial = multiplyMaterial; - else if (blendMode == BlendMode.Screen && screenMaterial) - usedMaterial = screenMaterial; - - usedMaterial = submeshGraphic.GetModifiedMaterial(usedMaterial); - canvasRenderer.SetMaterial(usedMaterial, usedTexture); -#if HAS_CULL_TRANSPARENT_MESH - canvasRenderer.cullTransparentMesh = allowCullTransparentMesh ? - mainCullTransparentMesh : false; -#endif + if (useOriginalTextureAndMaterial && hasBlendModeMaterials) { + bool allowCullTransparentMesh = true; + BlendMode materialBlendMode = blendModeMaterials.BlendModeForMaterial(usedMaterialItems[i]); + if ((materialBlendMode == BlendMode.Normal && submeshInstructionItem.hasPMAAdditiveSlot) || + (materialBlendMode == BlendMode.Additive && pmaVertexColors)) { + allowCullTransparentMesh = false; } - } else { - var originalTexture = submeshMaterial.mainTexture; - Material usedMaterial; - Texture usedTexture; - if (!customMaterialOverride.TryGetValue(originalTexture, out usedMaterial)) - usedMaterial = material; - if (!customTextureOverride.TryGetValue(originalTexture, out usedTexture)) - usedTexture = originalTexture; - - usedMaterial = submeshGraphic.GetModifiedMaterial(usedMaterial); - canvasRenderer.SetMaterial(usedMaterial, usedTexture); +#if HAS_CULL_TRANSPARENT_MESH + canvasRenderer.cullTransparentMesh = allowCullTransparentMesh ? + mainCullTransparentMesh : false; +#endif } + canvasRenderer.materialCount = 1; + if (assignAtCanvasRenderer) + canvasRenderer.SetMaterial(usedMaterialItems[i], usedTextureItems[i]); } + if (assignMeshOverrideMultiple != null) + assignMeshOverrideMultiple(submeshCount, meshesItems, usedMaterialItems, usedTextureItems); } protected void EnsureCanvasRendererCount (int targetCount) { @@ -856,6 +958,16 @@ namespace Spine.Unity { meshes.Add(SpineMesh.NewSkeletonMesh()); } + protected void EnsureUsedTexturesAndMaterialsCount (int targetCount) { + int oldCount = usedMaterials.Count; + usedMaterials.EnsureCapacity(targetCount); + usedTextures.EnsureCapacity(targetCount); + for (int i = oldCount; i < targetCount; i++) { + usedMaterials.Add(null); + usedTextures.Add(null); + } + } + protected void DestroyMeshes () { foreach (var mesh in meshes) { #if UNITY_EDITOR diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MathUtilities.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MathUtilities.cs new file mode 100644 index 000000000..1879e2d4b --- /dev/null +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MathUtilities.cs @@ -0,0 +1,71 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated January 1, 2020. Replaces all prior versions. + * + * Copyright (c) 2013-2022, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, + * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +using UnityEngine; + +namespace Spine.Unity { + public static class MathUtilities { + public static float InverseLerp (float a, float b, float value) { + return (value - a) / (b - a); + } + + /// + /// Returns the linear interpolation ratio of a to b that value lies on. + /// This is the t value that fulfills value = lerp(a, b, t). + /// + public static Vector2 InverseLerp (Vector2 a, Vector2 b, Vector2 value) { + return new Vector2( + (value.x - a.x) / (b.x - a.x), + (value.y - a.y) / (b.y - a.y)); + } + + /// + /// Returns the linear interpolation ratio of a to b that value lies on. + /// This is the t value that fulfills value = lerp(a, b, t). + /// + public static Vector3 InverseLerp (Vector3 a, Vector3 b, Vector3 value) { + return new Vector3( + (value.x - a.x) / (b.x - a.x), + (value.y - a.y) / (b.y - a.y), + (value.z - a.z) / (b.z - a.z)); + } + + /// + /// Returns the linear interpolation ratio of a to b that value lies on. + /// This is the t value that fulfills value = lerp(a, b, t). + /// + public static Vector4 InverseLerp (Vector4 a, Vector4 b, Vector4 value) { + return new Vector4( + (value.x - a.x) / (b.x - a.x), + (value.y - a.y) / (b.y - a.y), + (value.z - a.z) / (b.z - a.z), + (value.w - a.w) / (b.w - a.w)); + } + } +} diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MathUtilities.cs.meta b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MathUtilities.cs.meta new file mode 100644 index 000000000..9e0d973d7 --- /dev/null +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MathUtilities.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ef2b5da9383ed474d895b702a9baf79e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 005e714534004213c7095334aad81cf66168977c Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Tue, 2 Aug 2022 16:19:34 +0200 Subject: [PATCH 11/18] [unity] Fixed compile error of previous commit regarding Vector2Int on Unity 2017.1. --- .../SkeletonRenderTexture/SkeletonGraphicRenderTexture.cs | 6 ++++++ .../SkeletonRenderTexture/SkeletonRenderTextureBase.cs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonGraphicRenderTexture.cs b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonGraphicRenderTexture.cs index af30ad8e7..5587bced2 100644 --- a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonGraphicRenderTexture.cs +++ b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonGraphicRenderTexture.cs @@ -27,6 +27,10 @@ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +#if UNITY_2017_2_OR_NEWER +#define HAS_VECTOR2INT +#endif + using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; @@ -44,6 +48,7 @@ namespace Spine.Unity.Examples { /// [RequireComponent(typeof(SkeletonGraphic))] public class SkeletonGraphicRenderTexture : SkeletonRenderTextureBase { +#if HAS_VECTOR2INT [System.Serializable] public struct TextureMaterialPair { public Texture texture; @@ -249,5 +254,6 @@ namespace Spine.Unity.Examples { protected override void AssignMeshAtRenderer () { quadCanvasRenderer.SetMesh(quadMesh); } +#endif // HAS_VECTOR2INT } } diff --git a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs index 8842385ff..bdd84d153 100644 --- a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs +++ b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs @@ -31,9 +31,14 @@ using System; using UnityEngine; using UnityEngine.Rendering; +#if UNITY_2017_2_OR_NEWER +#define HAS_VECTOR2INT +#endif + namespace Spine.Unity.Examples { public abstract class SkeletonRenderTextureBase : MonoBehaviour { +#if HAS_VECTOR2INT public Color color = Color.white; public int maxRenderTextureSize = 1024; public GameObject quad; @@ -143,5 +148,6 @@ namespace Spine.Unity.Examples { } protected abstract void AssignMeshAtRenderer (); +#endif // HAS_VECTOR2INT } } From 10d71afe57b0df245700ee0bb8db8bc8019e7499 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Tue, 2 Aug 2022 16:24:24 +0200 Subject: [PATCH 12/18] [unity] Minor fix of last commit. --- .../SkeletonRenderTexture/SkeletonRenderTextureBase.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs index bdd84d153..0413d8b4a 100644 --- a/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs +++ b/spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonRenderTexture/SkeletonRenderTextureBase.cs @@ -27,14 +27,14 @@ * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -using System; -using UnityEngine; -using UnityEngine.Rendering; - #if UNITY_2017_2_OR_NEWER #define HAS_VECTOR2INT #endif +using System; +using UnityEngine; +using UnityEngine.Rendering; + namespace Spine.Unity.Examples { public abstract class SkeletonRenderTextureBase : MonoBehaviour { From 4386cb8c8eca5f1583845266b1ff21a54e811c40 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Wed, 3 Aug 2022 07:14:08 +0200 Subject: [PATCH 13/18] Update README.md --- spine-godot/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spine-godot/README.md b/spine-godot/README.md index bb4609b03..4a987cbf5 100644 --- a/spine-godot/README.md +++ b/spine-godot/README.md @@ -2,7 +2,7 @@ The spine-godot runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [Godot](https://godotengine.org/). spine-godot is based on [spine-cpp](../spine-cpp). -# See the [spine-godot documentation](http://esotericsoftware.com/spine-godot]) for in-depth information. +# See the [spine-godot documentation](http://esotericsoftware.com/spine-godot) for in-depth information. ## Licensing From b0a609f027ce612bd44003bc9d59c0463163361c Mon Sep 17 00:00:00 2001 From: Pol <36154612+PolRecasensSarra@users.noreply.github.com> Date: Wed, 3 Aug 2022 18:31:40 +0200 Subject: [PATCH 14/18] Corrected Tint black (#2122) Tint black wasn't working correctly because the red channelwas discarded by error, replacing it by the green channel. --- spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp b/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp index 8184ff13b..80a88c8d0 100644 --- a/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp +++ b/spine-cpp/spine-cpp/src/spine/SkeletonJson.cpp @@ -967,7 +967,7 @@ Animation *SkeletonJson::readAnimation(Json *root, SkeletonData *skeletonData) { toColor(color2, Json::getString(keyMap, "dark", 0), false); for (frame = 0, bezier = 0;; ++frame) { - timeline->setFrame(frame, time, color.r, color.g, color.b, color.a, color2.g, color2.g, color2.b); + timeline->setFrame(frame, time, color.r, color.g, color.b, color.a, color2.r, color2.g, color2.b); nextMap = keyMap->_next; if (!nextMap) { // timeline.shrink(); // BOZO From 1485bb47087caccc8a5171f990806c1b5b907858 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 3 Aug 2022 19:16:07 +0200 Subject: [PATCH 15/18] [unity] Minor cleanup: removed a leftover resolved fixxme line. --- .../Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs index 01f9aa995..64dd609f8 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonGraphic.cs @@ -459,7 +459,6 @@ namespace Spine.Unity { /// Rebuild, so you may want to subscribe to instead /// from where you can issue such preparation calls. public event SkeletonRendererDelegate OnMeshAndMaterialsUpdated; - //FIXXME: perform some final checks on documentation and changes, then finalize these changes and commit. then integrate in 4.0_cleanup project as well. protected Spine.AnimationState state; public Spine.AnimationState AnimationState { From d9696997acaca7ebee0e9879b6fa2d2b385c44c8 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 5 Aug 2022 08:50:04 +0200 Subject: [PATCH 16/18] [threejs] Fixes #2108, create CanvasTexture for ImageBitmap. --- spine-ts/spine-threejs/src/ThreeJsTexture.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spine-ts/spine-threejs/src/ThreeJsTexture.ts b/spine-ts/spine-threejs/src/ThreeJsTexture.ts index aef066548..be48c3013 100644 --- a/spine-ts/spine-threejs/src/ThreeJsTexture.ts +++ b/spine-ts/spine-threejs/src/ThreeJsTexture.ts @@ -35,8 +35,10 @@ export class ThreeJsTexture extends Texture { constructor (image: HTMLImageElement | ImageBitmap) { super(image); - if (image instanceof ImageBitmap) throw new Error("ImageBitmap not supported."); - this.texture = new THREE.Texture(image); + if (image instanceof ImageBitmap) + this.texture = new THREE.CanvasTexture(image); + else + this.texture = new THREE.Texture(image); this.texture.flipY = false; this.texture.needsUpdate = true; } From 3e0349e94ff61a8178cf67ac271a9f6dee7d5e74 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 5 Aug 2022 08:51:03 +0200 Subject: [PATCH 17/18] [c] Formatting. --- spine-sdl/example/main.c | 4 ++-- spine-sdl/example/main.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spine-sdl/example/main.c b/spine-sdl/example/main.c index 8992a0ef1..fa010d513 100644 --- a/spine-sdl/example/main.c +++ b/spine-sdl/example/main.c @@ -51,14 +51,14 @@ int main() { json->scale = 0.5f; spSkeletonData *skeletonData = spSkeletonJson_readSkeletonDataFile(json, "data/spineboy-pro.json"); spAnimationStateData *animationStateData = spAnimationStateData_create(skeletonData); - animationStateData->defaultMix = 0.2f; + animationStateData->defaultMix = 0.2f; spSkeletonDrawable *drawable = spSkeletonDrawable_create(skeletonData, animationStateData); drawable->skeleton->x = 400; drawable->skeleton->y = 500; spSkeleton_setToSetupPose(drawable->skeleton); spSkeletonDrawable_update(drawable, 0); spAnimationState_setAnimationByName(drawable->animationState, 0, "portal", 0); - spAnimationState_addAnimationByName(drawable->animationState, 0, "run", -1, 0); + spAnimationState_addAnimationByName(drawable->animationState, 0, "run", -1, 0); int quit = 0; uint64_t lastFrameTime = SDL_GetPerformanceCounter(); diff --git a/spine-sdl/example/main.cpp b/spine-sdl/example/main.cpp index 50083a6b8..89a69a556 100644 --- a/spine-sdl/example/main.cpp +++ b/spine-sdl/example/main.cpp @@ -53,11 +53,11 @@ int main() { json.setScale(0.5f); spine::SkeletonData *skeletonData = json.readSkeletonDataFile("data/spineboy-pro.json"); spine::SkeletonDrawable drawable(skeletonData); - drawable.animationState->getData()->setDefaultMix(0.2f); + drawable.animationState->getData()->setDefaultMix(0.2f); drawable.skeleton->setPosition(400, 500); drawable.skeleton->setToSetupPose(); drawable.update(0); - drawable.animationState->setAnimation(0, "portal", true); + drawable.animationState->setAnimation(0, "portal", true); drawable.animationState->addAnimation(0, "run", true, 0); bool quit = false; From 3387a470b3ba64e7fdeacb5d2ef0d0f10ecf4cdf Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Fri, 5 Aug 2022 08:53:39 +0200 Subject: [PATCH 18/18] [ts] Release 4.1.20. --- spine-ts/package-lock.json | 54 ++++++++++++++--------------- spine-ts/package.json | 2 +- spine-ts/spine-canvas/package.json | 4 +-- spine-ts/spine-core/package.json | 2 +- spine-ts/spine-player/package.json | 4 +-- spine-ts/spine-threejs/package.json | 4 +-- spine-ts/spine-webgl/package.json | 4 +-- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/spine-ts/package-lock.json b/spine-ts/package-lock.json index 1699c752f..93b24fe59 100644 --- a/spine-ts/package-lock.json +++ b/spine-ts/package-lock.json @@ -1,12 +1,12 @@ { "name": "@esotericsoftware/spine-ts", - "version": "4.1.19", + "version": "4.1.20", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@esotericsoftware/spine-ts", - "version": "4.1.19", + "version": "4.1.20", "license": "LicenseRef-LICENSE", "workspaces": [ "spine-core", @@ -603,9 +603,9 @@ } }, "node_modules/date-fns": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", - "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.1.tgz", + "integrity": "sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw==", "dev": true, "engines": { "node": ">=0.11" @@ -1129,9 +1129,9 @@ } }, "node_modules/http-parser-js": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", - "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", "dev": true }, "node_modules/inflight": { @@ -7967,41 +7967,41 @@ }, "spine-canvas": { "name": "@esotericsoftware/spine-canvas", - "version": "4.1.19", + "version": "4.1.20", "license": "LicenseRef-LICENSE", "dependencies": { - "@esotericsoftware/spine-core": "^4.1.19" + "@esotericsoftware/spine-core": "^4.1.20" } }, "spine-core": { "name": "@esotericsoftware/spine-core", - "version": "4.1.19", + "version": "4.1.20", "license": "LicenseRef-LICENSE" }, "spine-player": { "name": "@esotericsoftware/spine-player", - "version": "4.1.19", + "version": "4.1.20", "license": "LicenseRef-LICENSE", "dependencies": { - "@esotericsoftware/spine-webgl": "^4.1.19" + "@esotericsoftware/spine-webgl": "^4.1.20" } }, "spine-threejs": { "name": "@esotericsoftware/spine-threejs", - "version": "4.1.19", + "version": "4.1.20", "license": "LicenseRef-LICENSE", "dependencies": { - "@esotericsoftware/spine-core": "^4.1.19", + "@esotericsoftware/spine-core": "^4.1.20", "@types/three": "^0.140.0", "three": "^0.140.0" } }, "spine-webgl": { "name": "@esotericsoftware/spine-webgl", - "version": "4.1.19", + "version": "4.1.20", "license": "LicenseRef-LICENSE", "dependencies": { - "@esotericsoftware/spine-core": "^4.1.19" + "@esotericsoftware/spine-core": "^4.1.20" } } }, @@ -8009,7 +8009,7 @@ "@esotericsoftware/spine-canvas": { "version": "file:spine-canvas", "requires": { - "@esotericsoftware/spine-core": "^4.1.19" + "@esotericsoftware/spine-core": "^4.1.20" } }, "@esotericsoftware/spine-core": { @@ -8018,13 +8018,13 @@ "@esotericsoftware/spine-player": { "version": "file:spine-player", "requires": { - "@esotericsoftware/spine-webgl": "^4.1.19" + "@esotericsoftware/spine-webgl": "^4.1.20" } }, "@esotericsoftware/spine-threejs": { "version": "file:spine-threejs", "requires": { - "@esotericsoftware/spine-core": "^4.1.19", + "@esotericsoftware/spine-core": "^4.1.20", "@types/three": "^0.140.0", "three": "^0.140.0" } @@ -8032,7 +8032,7 @@ "@esotericsoftware/spine-webgl": { "version": "file:spine-webgl", "requires": { - "@esotericsoftware/spine-core": "^4.1.19" + "@esotericsoftware/spine-core": "^4.1.20" } }, "@types/offscreencanvas": { @@ -8484,9 +8484,9 @@ } }, "date-fns": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.28.0.tgz", - "integrity": "sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==", + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.1.tgz", + "integrity": "sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw==", "dev": true }, "debug": { @@ -8900,9 +8900,9 @@ } }, "http-parser-js": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.6.tgz", - "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", "dev": true }, "inflight": { diff --git a/spine-ts/package.json b/spine-ts/package.json index dcd0b7f75..df1b7aeef 100644 --- a/spine-ts/package.json +++ b/spine-ts/package.json @@ -1,6 +1,6 @@ { "name": "@esotericsoftware/spine-ts", - "version": "4.1.19", + "version": "4.1.20", "description": "The official Spine Runtimes for the web.", "files": [ "README.md" diff --git a/spine-ts/spine-canvas/package.json b/spine-ts/spine-canvas/package.json index 5f0cf533d..4e70671e0 100644 --- a/spine-ts/spine-canvas/package.json +++ b/spine-ts/spine-canvas/package.json @@ -1,6 +1,6 @@ { "name": "@esotericsoftware/spine-canvas", - "version": "4.1.19", + "version": "4.1.20", "description": "The official Spine Runtimes for the web.", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -30,6 +30,6 @@ }, "homepage": "https://github.com/esotericsoftware/spine-runtimes#readme", "dependencies": { - "@esotericsoftware/spine-core": "^4.1.19" + "@esotericsoftware/spine-core": "^4.1.20" } } \ No newline at end of file diff --git a/spine-ts/spine-core/package.json b/spine-ts/spine-core/package.json index d924a7fe2..6cfcd02c3 100644 --- a/spine-ts/spine-core/package.json +++ b/spine-ts/spine-core/package.json @@ -1,6 +1,6 @@ { "name": "@esotericsoftware/spine-core", - "version": "4.1.19", + "version": "4.1.20", "description": "The official Spine Runtimes for the web.", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/spine-ts/spine-player/package.json b/spine-ts/spine-player/package.json index b64718b6a..5ba5ada69 100644 --- a/spine-ts/spine-player/package.json +++ b/spine-ts/spine-player/package.json @@ -1,6 +1,6 @@ { "name": "@esotericsoftware/spine-player", - "version": "4.1.19", + "version": "4.1.20", "description": "The official Spine Runtimes for the web.", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -30,6 +30,6 @@ }, "homepage": "https://github.com/esotericsoftware/spine-runtimes#readme", "dependencies": { - "@esotericsoftware/spine-webgl": "^4.1.19" + "@esotericsoftware/spine-webgl": "^4.1.20" } } \ No newline at end of file diff --git a/spine-ts/spine-threejs/package.json b/spine-ts/spine-threejs/package.json index 7d6265c91..a50b9746e 100644 --- a/spine-ts/spine-threejs/package.json +++ b/spine-ts/spine-threejs/package.json @@ -1,6 +1,6 @@ { "name": "@esotericsoftware/spine-threejs", - "version": "4.1.19", + "version": "4.1.20", "description": "The official Spine Runtimes for the web.", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -32,6 +32,6 @@ "dependencies": { "@types/three": "^0.140.0", "three": "^0.140.0", - "@esotericsoftware/spine-core": "^4.1.19" + "@esotericsoftware/spine-core": "^4.1.20" } } \ No newline at end of file diff --git a/spine-ts/spine-webgl/package.json b/spine-ts/spine-webgl/package.json index 7a9f57201..f4ad4e80b 100644 --- a/spine-ts/spine-webgl/package.json +++ b/spine-ts/spine-webgl/package.json @@ -1,6 +1,6 @@ { "name": "@esotericsoftware/spine-webgl", - "version": "4.1.19", + "version": "4.1.20", "description": "The official Spine Runtimes for the web.", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -30,6 +30,6 @@ }, "homepage": "https://github.com/esotericsoftware/spine-runtimes#readme", "dependencies": { - "@esotericsoftware/spine-core": "^4.1.19" + "@esotericsoftware/spine-core": "^4.1.20" } } \ No newline at end of file