SDL2_image-2.8.8/.gitmodules0000664000000000000000000000171614757625540012530 0ustar00[submodule "external/jpeg"] path = external/jpeg url = https://github.com/libsdl-org/jpeg.git branch = v9f-SDL [submodule "external/libpng"] path = external/libpng url = https://github.com/libsdl-org/libpng.git branch = v1.6.47-SDL [submodule "external/libwebp"] path = external/libwebp url = https://github.com/libsdl-org/libwebp.git branch = 1.0.3-SDL [submodule "external/libtiff"] path = external/libtiff url = https://github.com/libsdl-org/libtiff.git branch = v4.2.0-SDL [submodule "external/zlib"] path = external/zlib url = https://github.com/libsdl-org/zlib.git branch = v1.3.1-SDL [submodule "external/libjxl"] path = external/libjxl url = https://github.com/libsdl-org/libjxl.git branch = v0.7.2-SDL [submodule "external/libavif"] path = external/libavif url = https://github.com/libsdl-org/libavif.git branch = v1.0.4-SDL [submodule "external/dav1d"] path = external/dav1d url = https://github.com/libsdl-org/dav1d.git branch = 1.2.1-SDL SDL2_image-2.8.8/.wikiheaders-options0000664000000000000000000000151214751443772014335 0ustar00projectfullname = SDL_image projectshortname = SDL_image incsubdir = include wikisubdir = SDL_image apiprefixregex = IMG_ mainincludefname = SDL_image.h versionfname = include/SDL_image.h versionmajorregex = \A\#define\s+SDL_IMAGE_MAJOR_VERSION\s+(\d+)\Z versionminorregex = \A\#define\s+SDL_IMAGE_MINOR_VERSION\s+(\d+)\Z versionpatchregex = \A\#define\s+SDL_IMAGE_PATCHLEVEL\s+(\d+)\Z selectheaderregex = \ASDL_image\.h\Z projecturl = https://libsdl.org/projects/SDL_image wikiurl = https://wiki.libsdl.org/SDL_image bugreporturl = https://github.com/libsdl-org/sdlwiki/issues/new warn_about_missing = 0 wikipreamble = (This function is part of SDL_image, a separate library from SDL.) wikiheaderfiletext = Defined in [<%fname%>](https://github.com/libsdl-org/SDL_image/blob/SDL2/include/%fname%) manpageheaderfiletext = Defined in %fname% SDL2_image-2.8.8/Android.mk0000664000000000000000000001117014730005212012232 0ustar00SDL_IMAGE_LOCAL_PATH := $(call my-dir) # Enable this if you want PNG and JPG support with minimal dependencies USE_STBIMAGE ?= true # The additional formats below require downloading third party dependencies, # using the external/download.sh script. # Enable this if you want to support loading AVIF images # The library path should be a relative path to this directory. SUPPORT_AVIF ?= false AVIF_LIBRARY_PATH := external/libavif DAV1D_LIBRARY_PATH := external/dav1d # Enable this if you want to support loading JPEG images using libjpeg # The library path should be a relative path to this directory. SUPPORT_JPG ?= false SUPPORT_SAVE_JPG ?= true JPG_LIBRARY_PATH := external/jpeg # Enable this if you want to support loading JPEG-XL images # The library path should be a relative path to this directory. SUPPORT_JXL ?= false JXL_LIBRARY_PATH := external/libjxl # Enable this if you want to support loading PNG images using libpng # The library path should be a relative path to this directory. SUPPORT_PNG ?= false SUPPORT_SAVE_PNG ?= true PNG_LIBRARY_PATH := external/libpng # Enable this if you want to support loading WebP images # The library path should be a relative path to this directory. SUPPORT_WEBP ?= false WEBP_LIBRARY_PATH := external/libwebp # Build the library ifeq ($(SUPPORT_AVIF),true) include $(SDL_IMAGE_LOCAL_PATH)/$(AVIF_LIBRARY_PATH)/Android.mk include $(SDL_IMAGE_LOCAL_PATH)/$(DAV1D_LIBRARY_PATH)/Android.mk endif # Build the library ifeq ($(SUPPORT_JPG),true) include $(SDL_IMAGE_LOCAL_PATH)/$(JPG_LIBRARY_PATH)/Android.mk endif # Build the library ifeq ($(SUPPORT_JXL),true) include $(SDL_IMAGE_LOCAL_PATH)/$(JXL_LIBRARY_PATH)/Android.mk endif # Build the library ifeq ($(SUPPORT_PNG),true) include $(SDL_IMAGE_LOCAL_PATH)/$(PNG_LIBRARY_PATH)/Android.mk endif # Build the library ifeq ($(SUPPORT_WEBP),true) include $(SDL_IMAGE_LOCAL_PATH)/$(WEBP_LIBRARY_PATH)/Android.mk endif # Restore local path LOCAL_PATH := $(SDL_IMAGE_LOCAL_PATH) include $(CLEAR_VARS) LOCAL_MODULE := SDL2_image LOCAL_SRC_FILES := \ src/IMG.c \ src/IMG_avif.c \ src/IMG_bmp.c \ src/IMG_gif.c \ src/IMG_jpg.c \ src/IMG_jxl.c \ src/IMG_lbm.c \ src/IMG_pcx.c \ src/IMG_png.c \ src/IMG_pnm.c \ src/IMG_qoi.c \ src/IMG_stb.c \ src/IMG_svg.c \ src/IMG_tga.c \ src/IMG_tif.c \ src/IMG_webp.c \ src/IMG_WIC.c \ src/IMG_xcf.c \ src/IMG_xpm.c.arm \ src/IMG_xv.c LOCAL_C_INCLUDES += $(LOCAL_PATH)/include LOCAL_CFLAGS := -DLOAD_BMP -DLOAD_GIF -DLOAD_LBM -DLOAD_PCX -DLOAD_PNM \ -DLOAD_SVG -DLOAD_TGA -DLOAD_XCF -DLOAD_XPM -DLOAD_XV \ -DLOAD_QOI LOCAL_LDLIBS := LOCAL_STATIC_LIBRARIES := LOCAL_SHARED_LIBRARIES := SDL2 ifeq ($(USE_STBIMAGE),true) LOCAL_CFLAGS += -DLOAD_JPG -DLOAD_PNG -DUSE_STBIMAGE endif ifeq ($(SUPPORT_AVIF),true) LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(AVIF_LIBRARY_PATH)/include LOCAL_CFLAGS += -DLOAD_AVIF LOCAL_STATIC_LIBRARIES += avif LOCAL_WHOLE_STATIC_LIBRARIES += dav1d dav1d-8bit dav1d-16bit endif ifeq ($(SUPPORT_JPG),true) LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(JPG_LIBRARY_PATH) \ $(LOCAL_PATH)/$(JPG_LIBRARY_PATH)/android LOCAL_CFLAGS += -DLOAD_JPG LOCAL_STATIC_LIBRARIES += jpeg ifeq ($(SUPPORT_SAVE_JPG),true) LOCAL_CFLAGS += -DSDL_IMAGE_SAVE_JPG=1 else LOCAL_CFLAGS += -DSDL_IMAGE_SAVE_JPG=0 endif endif ifeq ($(SUPPORT_JXL),true) LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(JXL_LIBRARY_PATH)/lib/include \ $(LOCAL_PATH)/$(JXL_LIBRARY_PATH)/android LOCAL_CFLAGS += -DLOAD_JXL LOCAL_STATIC_LIBRARIES += jxl endif ifeq ($(SUPPORT_PNG),true) LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(PNG_LIBRARY_PATH) \ $(LOCAL_PATH)/$(PNG_LIBRARY_PATH)/android LOCAL_CFLAGS += -DLOAD_PNG LOCAL_STATIC_LIBRARIES += png LOCAL_LDLIBS += -lz ifeq ($(SUPPORT_SAVE_PNG),true) LOCAL_CFLAGS += -DSDL_IMAGE_SAVE_PNG=1 else LOCAL_CFLAGS += -DSDL_IMAGE_SAVE_PNG=0 endif endif ifeq ($(SUPPORT_WEBP),true) LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(WEBP_LIBRARY_PATH)/src LOCAL_CFLAGS += -DLOAD_WEBP LOCAL_STATIC_LIBRARIES += webpdemux LOCAL_STATIC_LIBRARIES += webp endif LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)/include include $(BUILD_SHARED_LIBRARY) ########################### # # SDL2_image static library # ########################### LOCAL_MODULE := SDL2_image_static LOCAL_MODULE_FILENAME := libSDL2_image LOCAL_LDLIBS := LOCAL_EXPORT_LDLIBS := include $(BUILD_STATIC_LIBRARY) SDL2_image-2.8.8/CHANGES.txt0000664000000000000000000002066714761417066012167 0ustar002.8.8: * Fixed alpha in less than 32-bit ICO and CUR images 2.8.6: * Fixed partial alpha in ICO and CUR images 2.8.5: * Handle WEBP animation composition 2.8.4: * Fixed a regression in the last release with certain grayscale PNG images 2.8.3: * Fixed handling of grayscale images with alpha 2.8.2: * Fixed crash loading LBM images * Automatically set the colorkey for indexed PNG images with transparency 2.8.1: * Indexed PNG images with alpha have blending automatically enabled * Fixed a crash in the 32-bit webp DLLs on Windows * Fixed issue extracting the tar release archives on Linux 2.8.0: * Added support for loading WEBP animations * PNG images with a palette are loaded as SDL surfaces with a palette 2.6.3: * Fixed loading of 4-bit .ico files 2.6.2: * Updated autotools to use ax_compute_relative_paths, fixing homebrew on macOS 2.6.1: * Fixed loading grayscale PNGs when using stb_image 2.6.0: * Added support for building with CMake * Added stb_image as the default backend for JPG and PNG images loading. To use libpng and libjpg instead, configure using --disable-stb-image * Added IMG_LoadSizedSVG_RW() * Added support for AVIF images (https://github.com/AOMediaCodec/libavif) * Added IMG_ReadXPMFromArrayToRGB888() * Added support for JXL images (https://jpegxl.info/) * Added support for QOI images (https://qoiformat.org/) * Fixed XCF regression introduced in 2.0.5 * Added support for loading animated GIFs * LoadBMP() now loads files using SDL2 * Allow using libwebpdecoder instead libwebp 2.0.5: Sam Lantinga - Wed Jun 19 07:30:51 PDT 2019 * Updated external libraries libpng-1.6.32, libwebp-1.0.2 Sam Lantinga - Tue Jun 11 00:17:01 PDT 2019 * Fixed a number of security issues: TALOS-2019-0820 TALOS-2019-0821 TALOS-2019-0841 TALOS-2019-0842 TALOS-2019-0843 TALOS-2019-0844 Sathyanarayanan Gunasekaran, Brian Palmer, Charlie Birks, Amadeus - Mon Jun 10 16:48:20 PDT 2019 * Ported SDL_image to emscripten 2.0.4: MichaХ‚ Janiszewski - Fri Sept 28 22:00:26 PST 2018 * Fixed memory issues in the XCF loader Ryan Gordon - Wed Sept 26 14:58:31 PST 2018 * Fixed a number of security issues, including TALOS-2018-0645 2.0.3: Ryan Gordon - Sun Jan 28 21:28:16 PST 2018 * Fixed a number of security issues: TALOS-2017-0488 TALOS-2017-0489 TALOS-2017-0490 TALOS-2017-0491 TALOS-2017-0497 TALOS-2017-0498 TALOS-2017-0499 Sam Lantinga - Sun Jan 28 21:24:10 PST 2018 * Added a dependency on SDL 2.0.8 2.0.2: Sam Lantinga - Sat Oct 21 23:42:28 PDT 2017 * Added simple SVG image support based on Nano SVG Sam Lantinga - Sat Oct 21 22:14:34 PDT 2017 * Updated external libraries jpeg-9b, libpng-1.6.32, libwebp-0.6.0, tiff-4.0.8 and zlib-1.2.11 Yves Younan - Fri, Oct 6, 2017 3:38:38 PM * Fixed security vulnerability in XCF image loader Alexey - Tue Sep 12 00:41:53 PDT 2017 * Added optional support for loading images using Windows Imaging Component Fabian Greffrath - Tue Sep 12 00:15:56 PDT 2017 * Added libpng save support for much smaller 8-bit images Alexey - Mon Sep 11 23:50:31 PDT 2017 * Added JPG save support when built with jpeglib IMG_SaveJPG() and IMG_SaveJPG_RW() 2.0.1: Jeffrey Carpenter - Sat Nov 29 12:06:05 2014 * Fixed image colorspace issue on iOS and Mac OS X Sam Lantinga - Sun Jun 15 17:33:46 2014 * Fixed support for transparency in XPM files Davide Coppola - Thu Apr 17 17:30:12 2014 * Fixed building JPEG support on Android David Ludwig - Tue Apr 01 19:40:35 2014 * Added support for building for Windows RT and Windows Phone Timur - Wed Dec 11 21:24:36 2013 * Fixed memory leak in webp image loading Patrice Mandin - Thu Nov 07 19:15:28 2013 * Fixed loading BMP files with large BITMAPINFOHEADER structures Sam Lantinga - Fri Oct 11 21:54:20 2013 * Fixed building with libpng 1.4 2.0.0: Sam Lantinga - Sun Jun 2 22:25:31 PDT 2013 * Added PNG save support based on miniz.c by Rich Geldreich IMG_SavePNG(), IMG_SavePNG_RW() Sam Lantinga - Sat Jun 1 19:11:26 PDT 2013 * Updated for SDL 2.0 release Sam Lantinga - Sat Mar 23 13:36:51 PDT 2013 * Fixed bug setting colorkey for indexed PNG images Torsten Stremlau - Sun Mar 10 10:19:25 PDT 2013 * Added support for alpha and lossless WEBP images 1.2.12: mscott - 2012-02-06 19:40:23 PST * Fixed image corruption when using ImageIO framework Sylvain - Thu Nov 22 13:09:59 PST 2012 * Added extended XPM color table (disabled by default in IMG_xpm.c) Sam Lantinga - Thu Jan 19 23:18:09 EST 2012 * Fixed regression in 1.2.11 loading 8-bit PNG images with libpng 1.2.11: Sam Lantinga - Sat Jan 14 17:54:38 EST 2012 * Fixed loading 8-bit PNG images on Mac OS X Sam Lantinga - Sat Dec 31 09:35:40 EST 2011 * SDL_image is now under the zlib license Michael Bonfils - Mon Nov 28 21:46:00 EST 2011 * Added WEBP image support Thomas Klausner - Wed Jan 19 19:31:25 PST 2011 * Fixed compiling with libpng 1.4 Sam Lantinga - Mon Jan 10 12:09:57 2011 -0800 * Added Android.mk to build on the Android platform Sam Lantinga - Mon May 10 22:42:53 PDT 2010 * Fixed loading HAM6 images with stencil mask Mark Tucker - Fri, 27 Nov 2009 12:38:21 -0500 * Fixed bug loading 15 and 16 bit BMP images 1.2.10: Sam Lantinga - Sat Nov 14 11:22:14 PST 2009 * Fixed bug loading multiple images 1.2.9: Sam Lantinga - Tue Nov 10 00:29:20 PST 2009 * Fixed alpha premultiplication on Mac OS X and iPhone OS Sam Lantinga - Sun Nov 8 07:52:11 PST 2009 * Fixed checking for IMG_Init() return value in image loaders 1.2.8: Sam Lantinga - Sun Oct 4 13:12:54 PDT 2009 * Added support for uncompressed PCX files Mason Wheeler - 2009-06-10 06:29:45 PDT * Added IMG_Init()/IMG_Quit() to prevent constantly loading and unloading DLLs Couriersud - Mon, 12 Jan 2009 17:21:13 -0800 * Added support for ICO and CUR image files Eric Wing - Fri, 2 Jan 2009 02:01:16 -0800 * Added ImageIO loading infrastructure for Mac OS X * Added UIImage loading infrastructure for iPhone / iPod Touch 1.2.7: Sam Lantinga - Sun Nov 2 15:08:27 PST 2008 * Fixed buffer overflow in BMP loading code, discovered by j00ru//vx Sam Lantinga - Fri Dec 28 08:34:54 PST 2007 * Fixed buffer overflow in GIF loading code, discovered by Michael Skladnikiewicz 1.2.6: Sam lantinga - Wed Jul 18 00:30:32 PDT 2007 * Improved detection of libjpeg, libpng, and libtiff at configure time * PNG and TIFF images are correctly identified even if dynamic libraries to load them aren't available. * Fixed loading of TIFF images using libtiff 3.6 Sam Lantinga - Thu Jul 5 07:52:35 2007 * Fixed static linking with libjpeg Michael Koch - Tue Feb 13 10:09:17 2007 * Fixed crash in IMG_ReadXPMFromArray() 1.2.5: Maurizio Monge - Sun May 14 13:57:32 PDT 2006 * Fixed loading BMP palettes at unusual offsets Sam Lantinga - Thu May 11 21:51:19 PDT 2006 * Added support for dynamically loading libjpeg, libpng, and libtiff. Sam Lantinga - Sun Apr 30 01:48:40 PDT 2006 * Added gcc-fat.sh for generating Universal binaries on Mac OS X * Updated libtool support to version 1.5.22 Sam Lantinga - Sat Feb 4 15:17:44 PST 2006 * Added support for XV thumbnail images Gautier Portet - Fri, 19 Mar 2004 17:35:12 +0100 * Added support for 32-bit BMP files with alpha 1.2.4: Pierre G. Richard - Fri, 30 Jul 2004 11:13:11 +0000 (UTC) * Added support for RLE encoded BMP files Marc Le Douarain - Fri, 26 Dec 2003 18:23:42 +0100 * Added EHB and HAM mode support to the ILBM loader Sam Lantinga - Wed Nov 19 00:23:44 PST 2003 * Updated libtool support for new mingw32 DLL build process Holger Schemel - Mon, 04 Aug 2003 21:50:52 +0200 * Fixed crash loading certain PCX images Kyle Davenport - Sat, 19 Apr 2003 17:13:31 -0500 * Added .la files to the development RPM, fixing RPM build on RedHat 8 1.2.3: Ryan C. Gordon - Sat, 8 Feb 2003 09:36:33 -0500 * Fixed memory leak with non-seekable SDL_RWops Marc Le Douarain - Sun, 22 Dec 2002 22:59:51 +0100 * Added 24-bit support to the ILBM format loader Sam Lantinga - Sun Oct 20 20:55:46 PDT 2002 * Added shared library support for MacOS X Pete Shinners - Thu Jun 20 10:05:54 PDT 2002 * The JPEG loader can now load EXIF format JPEG images Dag-Erling Smorgrav - Thu May 2 19:09:48 PDT 2002 * The XCF loader now ignores invisible layers and channels 1.2.2: Sam Lantinga - Sat Apr 13 07:49:47 PDT 2002 * Updated autogen.sh for new versions of automake * Specify the SDL API calling convention (C by default) SDL2_image-2.8.8/CMakeLists.txt0000664000000000000000000011776414761421756013124 0ustar00cmake_minimum_required(VERSION 3.16) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") # See docs/release_checklist.md set(MAJOR_VERSION 2) set(MINOR_VERSION 8) set(MICRO_VERSION 8) set(SDL_REQUIRED_VERSION 2.0.9) include(PrivateSdlFunctions) sdl_calculate_derived_version_variables() if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) message(FATAL_ERROR "Prevented in-tree built. Please create a build directory outside of the SDL_image source code and call cmake from there") endif() project(SDL2_image LANGUAGES C VERSION "${FULL_VERSION}" ) message(STATUS "Configuring ${PROJECT_NAME} ${PROJECT_VERSION}") if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) set(SDL2IMAGE_ROOTPROJECT ON) else() set(SDL2IMAGE_ROOTPROJECT OFF) endif() set(SDL2IMAGE_SAMPLES_DEFAULT ${SDL2IMAGE_ROOTPROJECT}) if(ANDROID) set(SDL2IMAGE_SAMPLES_DEFAULT OFF) endif() # Set defaults preventing destination file conflicts set(SDL2IMAGE_DEBUG_POSTFIX "d" CACHE STRING "Name suffix for debug builds") mark_as_advanced(SDL2IMAGE_DEBUG_POSTFIX) # Assume MSVC projects don't have a package manager and need vendored dependencies (by default). # Most other platforms have some kind of package manager. # FIXME: consider a package manager such as conan/vcpkg instead of vendoring if(MSVC) set(vendored_default ON) else() set(vendored_default OFF) endif() set(sdl2image_install_enableable ON) if ((TARGET SDL2 OR TARGET SDL2-static) AND SDL2_DISABLE_INSTALL) # Cannot install SDL2_image when SDL2 is built in same built, and is not installed. set(sdl2image_install_enableable OFF) endif() include(CMakeDependentOption) include(CMakePackageConfigHelpers) include(GNUInstallDirs) option(CMAKE_POSITION_INDEPENDENT_CODE "Build static libraries with -fPIC" ON) option(BUILD_SHARED_LIBS "Build the library as a shared library" ON) cmake_dependent_option(SDL2IMAGE_INSTALL "Enable SDL2_image install target" ${SDL2IMAGE_ROOTPROJECT} "${sdl2image_install_enableable}" OFF) option(SDL2IMAGE_DEPS_SHARED "Load dependencies dynamically" ON) option(SDL2IMAGE_VENDORED "Use vendored third-party libraries" ${vendored_default}) option(SDL2IMAGE_STRICT "Fail when a dependency could not be found" OFF) set(required "") set(fatal_error "STATUS") if(SDL2IMAGE_STRICT) set(required "REQUIRED") set(fatal_error "FATAL_ERROR") endif() option(SDL2IMAGE_SAMPLES "Build the SDL2_image sample program(s)" ${SDL2IMAGE_SAMPLES_DEFAULT}) cmake_dependent_option(SDL2IMAGE_SAMPLES_INSTALL "Install the SDL2_image sample program(s)" OFF "SDL2IMAGE_SAMPLES;SDL2IMAGE_INSTALL" OFF) option(SDL2IMAGE_TESTS "Build unit tests?" OFF) cmake_dependent_option(SDL2IMAGE_TESTS_INSTALL "Install unit tests?" OFF "SDL2IMAGE_TESTS;SDL2IMAGE_INSTALL" OFF) option(SDL2IMAGE_BACKEND_STB "Use stb_image for loading JPEG and PNG files" ON) cmake_dependent_option(SDL2IMAGE_BACKEND_WIC "Add WIC backend (Windows Imaging Component)" OFF WIN32 OFF) cmake_dependent_option(SDL2IMAGE_BACKEND_IMAGEIO "Use native Mac OS X frameworks for loading images" ON APPLE OFF) option(SDL2IMAGE_AVIF "Support loading AVIF images" ON) option(SDL2IMAGE_BMP "Support loading BMP images" ON) option(SDL2IMAGE_GIF "Support loading GIF images" ON) option(SDL2IMAGE_JPG "Support loading JPEG images" ON) option(SDL2IMAGE_JXL "Support loading JXL images" OFF) option(SDL2IMAGE_LBM "Support loading LBM images" ON) option(SDL2IMAGE_PCX "Support loading PCX images" ON) option(SDL2IMAGE_PNG "Support loading PNG images" ON) option(SDL2IMAGE_PNM "Support loading PNM images" ON) option(SDL2IMAGE_QOI "Support loading QOI images" ON) option(SDL2IMAGE_SVG "Support loading SVG images" ON) option(SDL2IMAGE_TGA "Support loading TGA images" ON) option(SDL2IMAGE_TIF "Support loading TIFF images" ON) option(SDL2IMAGE_WEBP "Support loading WEBP images" ON) option(SDL2IMAGE_XCF "Support loading XCF images" ON) option(SDL2IMAGE_XPM "Support loading XPM images" ON) option(SDL2IMAGE_XV "Support loading XV images" ON) cmake_dependent_option(SDL2IMAGE_JPG_SAVE "Add JPEG save support" ON SDL2IMAGE_JPG OFF) cmake_dependent_option(SDL2IMAGE_PNG_SAVE "Add PNG save support" ON SDL2IMAGE_PNG OFF) set(LIBAVIF_MINIMUM_VERSION "0.9.3") if(SDL2IMAGE_VENDORED AND SDL2IMAGE_AVIF) set(SDL2IMAGE_AVIF_VENDORED ON) else() set(SDL2IMAGE_AVIF_VENDORED OFF) endif() cmake_dependent_option(SDL2IMAGE_AVIF_SHARED "Dynamically load AVIF support (requires shared libavif)" ${SDL2IMAGE_DEPS_SHARED} SDL2IMAGE_AVIF OFF) if(SDL2IMAGE_AVIF_VENDORED) set(SDL2IMAGE_DAV1D ON) set(SDL2IMAGE_DAV1D_VENDORED ON) else() set(SDL2IMAGE_DAV1D OFF) set(SDL2IMAGE_DAV1D_VENDORED OFF) endif() if(SDL2IMAGE_AVIF_SHARED) set(SDL2IMAGE_DAV1D_SHARED ON) else() set(SDL2IMAGE_DAV1D_SHARED OFF) endif() if(SDL2IMAGE_VENDORED AND SDL2IMAGE_JPG AND NOT (SDL2IMAGE_BACKEND_WIC OR SDL2IMAGE_BACKEND_STB OR SDL2IMAGE_BACKEND_IMAGEIO)) set(SDL2IMAGE_JPG_VENDORED ON) else() set(SDL2IMAGE_JPG_VENDORED OFF) endif() cmake_dependent_option(SDL2IMAGE_JPG_SHARED "Dynamically load JPG support (requires shared libjpeg)" ${SDL2IMAGE_DEPS_SHARED} "SDL2IMAGE_JPG;NOT SDL2IMAGE_BACKEND_WIC;NOT SDL2IMAGE_BACKEND_STB;NOT SDL2IMAGE_BACKEND_IMAGEIO" OFF) if(SDL2IMAGE_VENDORED AND SDL2IMAGE_JXL) set(SDL2IMAGE_JXL_VENDORED ON) else() set(SDL2IMAGE_JXL_VENDORED OFF) endif() cmake_dependent_option(SDL2IMAGE_JXL_SHARED "Dynamically load JXL support (requires shared libjxl)" ${SDL2IMAGE_DEPS_SHARED} SDL2IMAGE_JXL OFF) if(SDL2IMAGE_VENDORED AND SDL2IMAGE_PNG AND NOT (SDL2IMAGE_BACKEND_WIC OR SDL2IMAGE_BACKEND_STB OR SDL2IMAGE_BACKEND_IMAGEIO)) set(SDL2IMAGE_PNG_VENDORED ON) else() set(SDL2IMAGE_PNG_VENDORED OFF) endif() cmake_dependent_option(SDL2IMAGE_PNG_SHARED "Dynamically load PNG support (requires shared libpng)" ${SDL2IMAGE_DEPS_SHARED} "SDL2IMAGE_PNG;NOT SDL2IMAGE_BACKEND_WIC;NOT SDL2IMAGE_BACKEND_STB;NOT SDL2IMAGE_BACKEND_IMAGEIO" OFF) if(SDL2IMAGE_VENDORED AND SDL2IMAGE_TIF) set(SDL2IMAGE_TIF_VENDORED ON) else() set(SDL2IMAGE_TIF_VENDORED OFF) endif() cmake_dependent_option(SDL2IMAGE_TIF_SHARED "Dynamically load TIFF support (requires shared libtiff)" ${SDL2IMAGE_DEPS_SHARED} SDL2IMAGE_TIF OFF) if(SDL2IMAGE_VENDORED AND SDL2IMAGE_WEBP) set(SDL2IMAGE_WEBP_VENDORED ON) else() set(SDL2IMAGE_WEBP_VENDORED OFF) endif() cmake_dependent_option(SDL2IMAGE_WEBP_SHARED "Dynamically load WEBP support (requires shared libwebp)" ${SDL2IMAGE_DEPS_SHARED} SDL2IMAGE_WEBP OFF) if(SDL2IMAGE_PNG_VENDORED) set(SDL2IMAGE_ZLIB ON) else() set(SDL2IMAGE_ZLIB OFF) endif() if(SDL2IMAGE_VENDORED AND SDL2IMAGE_PNG_VENDORED) set(SDL2IMAGE_ZLIB_VENDORED ON) else() set(SDL2IMAGE_ZLIB_VENDORED OFF) endif() if(SDL2IMAGE_PNG_SHARED) set(SDL2IMAGE_ZLIB_SHARED ON) else() set(SDL2IMAGE_ZLIB_SHARED OFF) endif() # Save BUILD_SHARED_LIBS variable set(SDL2IMAGE_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) if(SDL2IMAGE_BUILD_SHARED_LIBS) set(sdl2_image_export_name SDL2_image) set(sdl2_image_install_name_infix shared) set(sdl2_target_name SDL2::SDL2) else() set(sdl2_image_export_name SDL2_image-static) set(sdl2_image_install_name_infix static) set(sdl2_target_name SDL2::SDL2-static) endif() sdl_find_sdl2(${sdl2_target_name} ${SDL_REQUIRED_VERSION}) # Set PROJECT_VERSION of subprojects to "" if it's project call does not set VERSION cmake_policy(SET CMP0048 NEW) # Allow cmake_dependent_option to use "Full Condition Syntax" if(POLICY CMP0127) cmake_policy(SET CMP0127 NEW) endif() # OpenGL is required by dependencies of (dependencies of) some vendored libraries if(NOT DEFINED OpenGL_GL_PREFERENCE) set(OpenGL_GL_PREFERENCE GLVND) endif() add_library(SDL2_image src/IMG.c src/IMG_WIC.c src/IMG_avif.c src/IMG_bmp.c src/IMG_gif.c src/IMG_jpg.c src/IMG_jxl.c src/IMG_lbm.c src/IMG_pcx.c src/IMG_png.c src/IMG_pnm.c src/IMG_qoi.c src/IMG_stb.c src/IMG_svg.c src/IMG_tga.c src/IMG_tif.c src/IMG_webp.c src/IMG_xcf.c src/IMG_xpm.c src/IMG_xv.c ) add_library(SDL2_image::${sdl2_image_export_name} ALIAS SDL2_image) target_include_directories(SDL2_image PUBLIC "$" "$" "$" ) target_compile_definitions(SDL2_image PRIVATE BUILD_SDL SDL_BUILD_MAJOR_VERSION=${MAJOR_VERSION} SDL_BUILD_MINOR_VERSION=${MINOR_VERSION} SDL_BUILD_MICRO_VERSION=${MICRO_VERSION} ) target_link_libraries(SDL2_image PRIVATE $) if(WIN32 AND SDL2IMAGE_BUILD_SHARED_LIBS) target_sources(SDL2_image PRIVATE src/version.rc ) if(MINGW) target_link_options(SDL2_image PRIVATE -static-libgcc) endif() endif() set_target_properties(SDL2_image PROPERTIES DEFINE_SYMBOL DLL_EXPORT EXPORT_NAME ${sdl2_image_export_name} C_VISIBILITY_PRESET "hidden" ) if(NOT ANDROID) set_target_properties(SDL2_image PROPERTIES DEBUG_POSTFIX "${SDL2IMAGE_DEBUG_POSTFIX}" SOVERSION "${LT_MAJOR}" VERSION "${LT_VERSION}" ) if(APPLE) cmake_minimum_required(VERSION 3.17) set_target_properties(SDL2_image PROPERTIES MACHO_COMPATIBILITY_VERSION "${DYLIB_COMPATIBILITY_VERSION}" MACHO_CURRENT_VERSION "${DYLIB_CURRENT_VERSION}" ) endif() endif() if(SDL2IMAGE_BUILD_SHARED_LIBS AND (APPLE OR (UNIX AND NOT ANDROID))) add_custom_command(TARGET SDL2_image POST_BUILD COMMAND "${CMAKE_COMMAND}" -E create_symlink "$" "libSDL2_image$<$:${SDL2IMAGE_DEBUG_POSTFIX}>$" # BYPRODUCTS "libSDL2_image$<$:${SDL2IMAGE_DEBUG_POSTFIX}>$" # Needs CMake 3.20 WORKING_DIRECTORY "${PROJECT_BINARY_DIR}" ) endif() if(SDL2IMAGE_BUILD_SHARED_LIBS) if(WIN32 OR OS2) set_target_properties(SDL2_image PROPERTIES PREFIX "" ) endif() if(OS2) # OS/2 doesn't support a DLL name longer than 8 characters. set_target_properties(SDL2_image PROPERTIES OUTPUT_NAME "SDL2img" ) elseif(UNIX AND NOT ANDROID) set_target_properties(SDL2_image PROPERTIES OUTPUT_NAME "SDL2_image-${LT_RELEASE}" ) endif() else() if(MSVC OR (WATCOM AND (WIN32 OR OS2))) set_target_properties(SDL2_image PROPERTIES OUTPUT_NAME "SDL2_image-static" ) endif() endif() # Use `Compatible Interface Properties` to ensure a shared SDL2_image is built with a shared SDL2 if(SDL2IMAGE_BUILD_SHARED_LIBS) set_property(TARGET SDL2_image PROPERTY INTERFACE_SDL2_SHARED ${SDL2IMAGE_BUILD_SHARED_LIBS}) set_property(TARGET SDL2_image APPEND PROPERTY COMPATIBLE_INTERFACE_BOOL SDL2_SHARED) endif() if(SDL2IMAGE_BUILD_SHARED_LIBS) sdl_target_link_options_no_undefined(SDL2_image) endif() if(SDL2IMAGE_BUILD_SHARED_LIBS) # Make sure static library dependencies are built with -fPIC when building a shared SDL2_image set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif() set(INSTALL_EXTRA_TARGETS) set(PC_LIBS) set(PC_REQUIRES) set(SDL2IMAGE_BACKENDS) list(APPEND SDL2IMAGE_BACKENDS STB) set(SDL2IMAGE_STB_ENABLED FALSE) if(SDL2IMAGE_BACKEND_STB) set(SDL2IMAGE_STB_ENABLED TRUE) target_compile_definitions(SDL2_image PRIVATE USE_STBIMAGE) endif() list(APPEND SDL2IMAGE_BACKENDS IMAGEIO) set(SDL2IMAGE_IMAGEIO_ENABLED FALSE) if(APPLE) if(SDL2IMAGE_BACKEND_IMAGEIO) set(SDL2IMAGE_IMAGEIO_ENABLED TRUE) if(CMAKE_SYSTEM_NAME MATCHES ".*(Darwin|MacOS).*") target_link_libraries(SDL2_image PRIVATE -Wl,-framework,ApplicationServices) endif() target_link_libraries(SDL2_image PRIVATE objc) target_sources(SDL2_image PRIVATE src/IMG_ImageIO.m ) if (SDL2IMAGE_PNG AND NOT SDL2IMAGE_BACKEND_STB) target_compile_definitions(SDL2_image PRIVATE PNG_USES_IMAGEIO) endif() if (SDL2IMAGE_JPG AND NOT SDL2IMAGE_BACKEND_STB) target_compile_definitions(SDL2_image PRIVATE JPG_USES_IMAGEIO) endif() else() target_compile_definitions(SDL2_image PRIVATE SDL_IMAGE_USE_COMMON_BACKEND) endif() endif() list(APPEND SDL2IMAGE_BACKENDS WIC) set(SDL2IMAGE_WIC_ENABLED FALSE) if(SDL2IMAGE_BACKEND_WIC) set(SDL2IMAGE_WIC_ENABLED TRUE) target_compile_definitions(SDL2_image PRIVATE SDL_IMAGE_USE_WIC_BACKEND) endif() if(SDL2IMAGE_ZLIB) if(SDL2IMAGE_ZLIB_VENDORED) message(STATUS "${PROJECT_NAME}: Using vendored zlib") # disable build of zlib example programs: set(ZLIB_BUILD_EXAMPLES OFF CACHE BOOL "zlib examples" FORCE) sdl_check_project_in_subfolder(external/zlib zlib SDL2IMAGE_VENDORED) add_subdirectory(external/zlib EXCLUDE_FROM_ALL) set(ZLIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/external/zlib;${CMAKE_CURRENT_BINARY_DIR}/external/zlib") # ZLIB_INCLUDE_DIR variable is used by vendored libpng set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/external/zlib;${CMAKE_CURRENT_BINARY_DIR}/external/zlib" CACHE STRING "path of zlib, passed to libpng" FORCE) # ZLIB_LIBRARY variable is used by vendored libpng if(SDL2IMAGE_ZLIB_SHARED) set(ZLIB_LIBRARY zlib) else() set(ZLIB_LIBRARY zlibstatic) set(SKIP_INSTALL_CONFIG_FILE ON) endif() if(NOT TARGET ZLIB::ZLIB) add_library(ZLIB::ZLIB ALIAS ${ZLIB_LIBRARY}) endif() # ZLIB_INCLUDE_DIRS variable is used by vendored libpng set(ZLIB_INCLUDE_DIRS "${ZLIB_INCLUDE_DIR}") # ZLIB_LIBRARIES variable is used by vendored libpng set(ZLIB_LIBRARIES "${ZLIB_LIBRARY}") list(APPEND INSTALL_EXTRA_TARGETS ${ZLIB_LIBRARY}) set_target_properties(${ZLIB_LIBRARY} PROPERTIES EXPORT_NAME external_zlib) add_library(SDL2_image::external_zlib ALIAS ${ZLIB_LIBRARY}) else() message(FATAL_ERROR "Internal error (zlib is only required when requesting vendored dependencies)") endif() endif() if(SDL2IMAGE_DAV1D) if(SDL2IMAGE_DAV1D_VENDORED) message(STATUS "${PROJECT_NAME}: Using vendored dav1d") add_subdirectory(external/dav1d) set(DAV1D_LIBRARY dav1d) list(APPEND INSTALL_EXTRA_TARGETS dav1d) else() message(FATAL_ERROR "Internal error (dav1d is only required when requesting vendored dependencies)") endif() endif() list(APPEND SDL2IMAGE_BACKENDS AVIF) set(SDL2IMAGE_AVIF_ENABLED FALSE) if(SDL2IMAGE_AVIF) if(SDL2IMAGE_AVIF_VENDORED) set(SDL2IMAGE_AVIF_ENABLED TRUE) message(STATUS "${PROJECT_NAME}: Using vendored libavif") sdl_check_project_in_subfolder(external/libavif libavif SDL2IMAGE_VENDORED) set(BUILD_SHARED_LIBS ${SDL2IMAGE_AVIF_SHARED}) set(AVIF_CODEC_DAV1D ON CACHE BOOL "Use dav1d codec for decoding" FORCE) set(AVIF_LOCAL_DAV1D OFF CACHE BOOL "Build dav1d by libaf" FORCE) #FIXME: This requires upgrading vendored libwebp to >= 1.2.4. #set(LIBAVIF_WITH_SHARPYUV_SDLIMAGE ${SDL2IMAGE_WEBP} CACHE BOOL "Build libavif with sharpyuv support (re-use sharpyuv built by libwebp)" FORCE) add_subdirectory(external/libavif EXCLUDE_FROM_ALL) list(APPEND INSTALL_EXTRA_TARGETS avif) set_target_properties(avif PROPERTIES EXPORT_NAME external_libavif) add_library(SDL2_image::external_libavif ALIAS avif) if(NOT SDL2IMAGE_AVIF_SHARED) list(APPEND PC_LIBS -l$) endif() if(NOT MSVC AND NOT CMAKE_SYSTEM_NAME MATCHES ".*OpenBSD.*") check_linker_flag(C "-Wl,--no-undefined" LINKER_SUPPORTS_WL_NO_UNDEFINED) if(LINKER_SUPPORTS_WL_NO_UNDEFINED) target_link_options(avif PRIVATE "-Wl,--no-undefined") endif() endif() else() find_package(libavif 1.0 QUIET) if(NOT libavif_FOUND) message(STATUS "libavif-1.0 or compatible not found") find_package(libavif ${LIBAVIF_MINIMUM_VERSION} QUIET) endif() if(libavif_FOUND) set(SDL2IMAGE_AVIF_ENABLED TRUE) message(STATUS "${PROJECT_NAME}: Using system libavif") message(STATUS "libavif-${libavif_VERSION} found") if(NOT SDL2IMAGE_AVIF_SHARED) list(APPEND PC_REQUIRES libavif) endif() else() message(STATUS "libavif-${LIBAVIF_MINIMUM_VERSION} or compatible not found") message(${fatal_error} "libavif NOT found") endif() endif() if(SDL2IMAGE_AVIF_ENABLED) target_compile_definitions(SDL2_image PRIVATE LOAD_AVIF) if(SDL2IMAGE_AVIF_SHARED) target_include_directories(SDL2_image PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_avif avif) message(STATUS "Dynamic libavif: ${dynamic_avif}") target_compile_definitions(SDL2_image PRIVATE "LOAD_AVIF_DYNAMIC=\"${dynamic_avif}\"") if(SDL2IMAGE_AVIF_VENDORED) add_dependencies(SDL2_image avif) endif() else() target_link_libraries(SDL2_image PRIVATE avif) endif() endif() endif() list(APPEND SDL2IMAGE_BACKENDS BMP) set(SDL2IMAGE_BMP_ENABLED FALSE) if(SDL2IMAGE_BMP) set(SDL2IMAGE_BMP_ENABLED TRUE) target_compile_definitions(SDL2_image PRIVATE LOAD_BMP) endif() list(APPEND SDL2IMAGE_BACKENDS GIF) set(SDL2IMAGE_GIF_ENABLED FALSE) if(SDL2IMAGE_GIF) set(SDL2IMAGE_GIF_ENABLED TRUE) target_compile_definitions(SDL2_image PRIVATE LOAD_GIF) endif() list(APPEND SDL2IMAGE_BACKENDS JPG) set(SDL2IMAGE_JPG_ENABLED FALSE) if(SDL2IMAGE_JPG) if(SDL2IMAGE_BACKEND_STB OR SDL2IMAGE_BACKEND_WIC OR SDL2IMAGE_BACKEND_IMAGEIO) set(SDL2IMAGE_JPG_ENABLED TRUE) else() if(SDL2IMAGE_JPG_VENDORED) set(SDL2IMAGE_JPG_ENABLED TRUE) message(STATUS "${PROJECT_NAME}: Using vendored libjpeg") sdl_check_project_in_subfolder(external/jpeg libjpeg SDL2IMAGE_VENDORED) set(BUILD_SHARED_LIBS ${SDL2IMAGE_JPG_SHARED}) add_subdirectory(external/jpeg EXCLUDE_FROM_ALL) list(APPEND INSTALL_EXTRA_TARGETS jpeg) set_target_properties(jpeg PROPERTIES EXPORT_NAME external_libjpeg) add_library(SDL2_image::external_libjpeg ALIAS jpeg) if(NOT SDL2IMAGE_JPG_SHARED) list(APPEND PC_LIBS -l$) endif() else() find_package(JPEG ${required}) if(JPEG_FOUND) set(SDL2IMAGE_JPG_ENABLED TRUE) message(STATUS "${PROJECT_NAME}: Using system libjpeg") if(NOT SDL2IMAGE_JPG_SHARED) list(APPEND PC_REQUIRES libjpeg) endif() else() message(${fatal_error} "libjpeg NOT found") endif() endif() if(SDL2IMAGE_JPG_ENABLED) if(SDL2IMAGE_JPG_SHARED) target_include_directories(SDL2_image PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_jpeg JPEG::JPEG) message(STATUS "Dynamic libjpeg: ${dynamic_jpeg}") target_compile_definitions(SDL2_image PRIVATE "LOAD_JPG_DYNAMIC=\"${dynamic_jpeg}\"") if(SDL2IMAGE_JPG_VENDORED) add_dependencies(SDL2_image JPEG::JPEG) endif() else() target_link_libraries(SDL2_image PRIVATE JPEG::JPEG) endif() endif() endif() if(SDL2IMAGE_JPG_ENABLED) target_compile_definitions(SDL2_image PRIVATE LOAD_JPG SDL_IMAGE_SAVE_JPG=$ ) endif() endif() list(APPEND SDL2IMAGE_BACKENDS JXL) set(SDL2IMAGE_JXL_ENABLED FALSE) if(SDL2IMAGE_JXL) if(SDL2IMAGE_JXL_VENDORED) set(SDL2IMAGE_JXL_ENABLED TRUE) enable_language(CXX) message(STATUS "${PROJECT_NAME}: Using vendored libjxl") # BUILD_TESTING variable is used by libjxl set(BUILD_TESTING OFF CACHE BOOL "build testing" FORCE) # JPEGXL_ENABLE_TOOLS variable is used by libjxl set(JPEGXL_ENABLE_JNI OFF CACHE BOOL "build jpegxl jni" FORCE) # JPEGXL_ENABLE_MANPAGES variable is used by libjxl set(JPEGXL_ENABLE_MANPAGES OFF CACHE BOOL "libjxl manpage option" FORCE) # JPEGXL_ENABLE_PLUGINS variable is used by libjxl set(JPEGXL_ENABLE_PLUGINS OFF CACHE BOOL "libjxl manpage option" FORCE) # JPEGXL_ENABLE_SKCMS variable is used by libjxl set(JPEGXL_ENABLE_SKCMS OFF CACHE BOOL "libjxl skcms option" FORCE) # JPEGXL_FORCE_SYSTEM_HWY variable is used by libjxl set(JPEGXL_FORCE_SYSTEM_HWY OFF CACHE BOOL "libjxl highway option" FORCE) sdl_check_project_in_subfolder(external/libjxl libjxl SDL2IMAGE_VENDORED) set(BUILD_SHARED_LIBS ${SDL2IMAGE_JXL_SHARED}) add_subdirectory(external/libjxl EXCLUDE_FROM_ALL) if(BUILD_SHARED_LIBS) set(jxl_lib jxl) list(APPEND INSTALL_EXTRA_TARGETS brotlidec brotlicommon brotlienc hwy ${jxl_lib}) if(NOT SDL2IMAGE_JXL_SHARED) list(APPEND PC_LIBS -l$ -l$ -l$ -l$ ) endif() else() set(jxl_lib jxl_dec-static) list(APPEND INSTALL_EXTRA_TARGETS brotlidec brotlicommon hwy ${jxl_lib}) endif() set_target_properties(${jxl_lib} PROPERTIES EXPORT_NAME external_libjxl) add_library(SDL2_image::external_libjxl ALIAS ${jxl_lib}) if(NOT TARGET libjxl::libjxl) add_library(libjxl::libjxl ALIAS ${jxl_lib}) endif() else() find_package(libjxl ${required}) if(libjxl_FOUND) set(SDL2IMAGE_JXL_ENABLED TRUE) message(STATUS "${PROJECT_NAME}: Using system libjxl") if(NOT SDL2IMAGE_JXL_SHARED) list(APPEND PC_REQUIRES libjxl) endif() else() message(${fatal_error} "libjxl NOT found") endif() endif() if(SDL2IMAGE_JXL_ENABLED) target_compile_definitions(SDL2_image PRIVATE LOAD_JXL) if(SDL2IMAGE_JXL_SHARED) target_include_directories(SDL2_image PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_jxl libjxl::libjxl) message(STATUS "Dynamic libjxl: ${dynamic_jxl}") target_compile_definitions(SDL2_image PRIVATE "LOAD_JXL_DYNAMIC=\"${dynamic_jxl}\"") if(SDL2IMAGE_JXL_VENDORED) add_dependencies(SDL2_image libjxl::libjxl) endif() else() target_link_libraries(SDL2_image PRIVATE libjxl::libjxl) endif() endif() endif() list(APPEND SDL2IMAGE_BACKENDS LBM) set(SDL2IMAGE_LBM_ENABLED FALSE) if(SDL2IMAGE_LBM) set(SDL2IMAGE_LBM_ENABLED TRUE) target_compile_definitions(SDL2_image PRIVATE LOAD_LBM) endif() list(APPEND SDL2IMAGE_BACKENDS PCX) set(SDL2IMAGE_PCX_ENABLED FALSE) if(SDL2IMAGE_PCX) set(SDL2IMAGE_PCX_ENABLED TRUE) target_compile_definitions(SDL2_image PRIVATE LOAD_PCX) endif() list(APPEND SDL2IMAGE_BACKENDS PNG) set(SDL2IMAGE_PNG_ENABLED FALSE) if(SDL2IMAGE_PNG) if(SDL2IMAGE_BACKEND_STB OR SDL2IMAGE_BACKEND_WIC OR SDL2IMAGE_BACKEND_IMAGEIO) set(SDL2IMAGE_PNG_ENABLED TRUE) else() if(SDL2IMAGE_PNG_VENDORED) set(SDL2IMAGE_PNG_ENABLED TRUE) message(STATUS "${PROJECT_NAME}: Using vendored libpng") sdl_check_project_in_subfolder(external/libpng libpng SDL2IMAGE_VENDORED) add_subdirectory(external/libpng EXCLUDE_FROM_ALL) if(SDL2IMAGE_PNG_SHARED) set(PNG_LIBRARY png_shared) else() set(PNG_LIBRARY png_static) endif() add_library(PNG::PNG ALIAS ${PNG_LIBRARY}) target_include_directories(SDL2_image PRIVATE external/libpng) list(APPEND INSTALL_EXTRA_TARGETS ${PNG_LIBRARY}) set_target_properties(${PNG_LIBRARY} PROPERTIES EXPORT_NAME external_libpng) add_library(SDL2_image::external_libpng ALIAS ${PNG_LIBRARY}) if(NOT SDL2IMAGE_PNG_SHARED) list(APPEND PC_LIBS -l$) if(SDL2IMAGE_ZLIB_VENDORED) list(APPEND PC_LIBS -l$) else() list(APPEND PC_REQUIRES zlib) endif() endif() else() find_package(PNG ${required}) if(PNG_FOUND) set(SDL2IMAGE_PNG_ENABLED TRUE) message(STATUS "${PROJECT_NAME}: Using system libpng") if(NOT SDL2IMAGE_PNG_SHARED) list(APPEND PC_REQUIRES libpng) endif() else() message(${FATAL_ERROR} "libpng NOT found") endif() endif() if(SDL2IMAGE_PNG_ENABLED) if(SDL2IMAGE_PNG_SHARED) target_include_directories(SDL2_image PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_png PNG::PNG) message(STATUS "Dynamic libpng: ${dynamic_png}") target_compile_definitions(SDL2_image PRIVATE "LOAD_PNG_DYNAMIC=\"${dynamic_png}\"") if(SDL2IMAGE_PNG_VENDORED) add_dependencies(SDL2_image PNG::PNG) endif() else() target_link_libraries(SDL2_image PRIVATE PNG::PNG) endif() endif() endif() if(SDL2IMAGE_PNG_ENABLED) target_compile_definitions(SDL2_image PRIVATE LOAD_PNG SDL_IMAGE_SAVE_PNG=$ ) endif() endif() list(APPEND SDL2IMAGE_BACKENDS PNM) set(SDL2IMAGE_PNM_ENABLED FALSE) if(SDL2IMAGE_PNM) set(SDL2IMAGE_PNM_ENABLED TRUE) target_compile_definitions(SDL2_image PRIVATE LOAD_PNM) endif() list(APPEND SDL2IMAGE_BACKENDS QOI) set(SDL2IMAGE_QOI_ENABLED FALSE) if(SDL2IMAGE_QOI) set(SDL2IMAGE_QOI_ENABLED TRUE) target_compile_definitions(SDL2_image PRIVATE LOAD_QOI) endif() list(APPEND SDL2IMAGE_BACKENDS SVG) set(SDL2IMAGE_SVG_ENABLED FALSE) if(SDL2IMAGE_SVG) set(SDL2IMAGE_SVG_ENABLED TRUE) target_compile_definitions(SDL2_image PRIVATE LOAD_SVG) endif() list(APPEND SDL2IMAGE_BACKENDS TGA) set(SDL2IMAGE_TGA_ENABLED FALSE) if(SDL2IMAGE_TGA) set(SDL2IMAGE_TGA_ENABLED TRUE) target_compile_definitions(SDL2_image PRIVATE LOAD_TGA) endif() list(APPEND SDL2IMAGE_BACKENDS TIF) set(SDL2IMAGE_TIF_ENABLED FALSE) if(SDL2IMAGE_TIF) if(SDL2IMAGE_TIF_VENDORED) set(SDL2IMAGE_TIF_ENABLED TRUE) message(STATUS "${PROJECT_NAME}: Using vendored libtiff") # jpeg variable is used by vendored libtiff set(jpeg OFF CACHE BOOL "libtiff: jpeg option" FORCE) # libdeflate variable is used by vendored libtiff set(libdeflate OFF CACHE BOOL "libtiff: libdeflate option" FORCE) # DEFLATE_FOUND variable is used by vendored libtiff set(DEFLATE_FOUND OFF CACHE BOOL "libtiff: libdeflate option" FORCE) # zlib variable is used by vendored libtiff (controls use of `find_package`) set(zlib OFF CACHE BOOL "libtiff: find zlib using find_package" FORCE) # ZLIB_FOUND is used by vendored libtiff set(ZLIB_FOUND "") # lzma variable is used by vendored libtiff set(lzma OFF CACHE BOOL "libtiff: lzma option" FORCE) # webp variable is used by vendored libtiff set(webp OFF CACHE BOOL "libtiff: webp option" FORCE) # zstd variable is used by vendored libtiff set(zstd OFF CACHE BOOL "libtiff: zstd option" FORCE) # ZSTD_FOUND variable is used by vendored libtiff set(ZSTD_FOUND OFF) # WEBP_LIBRARY variable is used by vendored libtiff set(WEBP_LIBRARY "") sdl_check_project_in_subfolder(external/libtiff libtiff SDL2IMAGE_VENDORED) set(BUILD_SHARED_LIBS ${SDL2IMAGE_TIF_SHARED}) add_subdirectory(external/libtiff EXCLUDE_FROM_ALL) add_library(TIFF::TIFF ALIAS tiff) set(SDL2IMAGE_TIF_TARGET "TIFF::TIFF") list(APPEND INSTALL_EXTRA_TARGETS tiff) set_target_properties(tiff PROPERTIES EXPORT_NAME external_libtiff) add_library(SDL2_image::external_libtiff ALIAS tiff) if(NOT SDL2IMAGE_TIF_SHARED) list(APPEND PC_LIBS -l$) endif() else() find_package(TIFF ${required}) if(TIFF_FOUND) set(SDL2IMAGE_TIF_ENABLED TRUE) message(STATUS "${PROJECT_NAME}: Using system libtiff") if(NOT SDL2IMAGE_TIF_SHARED) list(APPEND PC_REQUIRES libtiff-4) endif() if(TARGET TIFF::tiff) # Introduced in CMake 3.28 # TIFF::TIFF still exists, but it is an INTERFACE library linking to TIFF::tiff (no ALIAS library) set(SDL2IMAGE_TIF_TARGET "TIFF::tiff") else() set(SDL2IMAGE_TIF_TARGET "TIFF::TIFF") endif() else() message(${fatal_error} "libtiff NOT found") endif() endif() if(SDL2IMAGE_TIF_ENABLED) target_compile_definitions(SDL2_image PRIVATE LOAD_TIF) if(SDL2IMAGE_TIF_SHARED) target_include_directories(SDL2_image PRIVATE $ $ $ ) target_get_dynamic_library(dynamic_tif ${SDL2IMAGE_TIF_TARGET}) message(STATUS "Dynamic libtiff: ${dynamic_tif}") target_compile_definitions(SDL2_image PRIVATE "LOAD_TIF_DYNAMIC=\"${dynamic_tif}\"") if(SDL2IMAGE_TIF_VENDORED) add_dependencies(SDL2_image ${SDL2IMAGE_TIF_TARGET}) endif() else() target_link_libraries(SDL2_image PRIVATE ${SDL2IMAGE_TIF_TARGET}) endif() endif() endif() list(APPEND SDL2IMAGE_BACKENDS WEBP) set(SDL2IMAGE_WEBP_ENABLED FALSE) if(SDL2IMAGE_WEBP) # missing cpufeatures if(SDL2IMAGE_WEBP_VENDORED) set(SDL2IMAGE_WEBP_ENABLED TRUE) message(STATUS "${PROJECT_NAME}: Using vendored libwebp") sdl_check_project_in_subfolder(external/libwebp libwebp SDL2IMAGE_VENDORED) set(BUILD_SHARED_LIBS ${SDL2IMAGE_WEBP_SHARED}) add_subdirectory(external/libwebp EXCLUDE_FROM_ALL) target_include_directories(SDL2_image PRIVATE external/libwebp/src) add_library(WebP::webp ALIAS webp) add_library(WebP::webpdemux ALIAS webpdemux) list(APPEND INSTALL_EXTRA_TARGETS webp webpdemux) set_target_properties(webp PROPERTIES EXPORT_NAME "external_libwebp") add_library(SDL2_image::external_libwebp ALIAS webp) else() find_package(webp ${required}) if(webp_FOUND) set(SDL2IMAGE_WEBP_ENABLED TRUE) message(STATUS "${PROJECT_NAME}: Using system libwebp") if(NOT SDL2IMAGE_WEBP_SHARED) list(APPEND PC_REQUIRES libwebp libwebpdemux) endif() else() message(${fatal_error} "libwebp NOT found") endif() endif() if(SDL2IMAGE_WEBP_ENABLED) target_compile_definitions(SDL2_image PRIVATE LOAD_WEBP) if(SDL2IMAGE_WEBP_SHARED) target_include_directories(SDL2_image PRIVATE $ $ $ $ $ $ ) target_get_dynamic_library(dynamic_webpdemux WebP::webpdemux) message(STATUS "Dynamic libwebpdemux: ${dynamic_webpdemux}") target_compile_definitions(SDL2_image PRIVATE "LOAD_WEBPDEMUX_DYNAMIC=\"${dynamic_webpdemux}\"") target_get_dynamic_library(dynamic_webp WebP::webp) message(STATUS "Dynamic libwebp: ${dynamic_webp}") target_compile_definitions(SDL2_image PRIVATE "LOAD_WEBP_DYNAMIC=\"${dynamic_webp}\"") if(SDL2IMAGE_WEBP_VENDORED) add_dependencies(SDL2_image WebP::webp WebP::webpdemux) endif() else() target_link_libraries(SDL2_image PRIVATE WebP::webp WebP::webpdemux) endif() endif() endif() list(APPEND SDL2IMAGE_BACKENDS XCF) set(SDL2IMAGE_XCF_ENABLED FALSE) if(SDL2IMAGE_XCF) set(SDL2IMAGE_XCF_ENABLED TRUE) target_compile_definitions(SDL2_image PRIVATE LOAD_XCF) endif() list(APPEND SDL2IMAGE_BACKENDS XPM) set(SDL2IMAGE_XPM_ENABLED FALSE) if(SDL2IMAGE_XPM) set(SDL2IMAGE_XPM_ENABLED TRUE) target_compile_definitions(SDL2_image PRIVATE LOAD_XPM) endif() list(APPEND SDL2IMAGE_BACKENDS XV) set(SDL2IMAGE_XV_ENABLED FALSE) if(SDL2IMAGE_XV) set(SDL2IMAGE_XV_ENABLED TRUE) target_compile_definitions(SDL2_image PRIVATE LOAD_XV) endif() # Restore BUILD_SHARED_LIBS set(BUILD_SHARED_LIBS ${SDL2IMAGE_BUILD_SHARED_LIBS}) if(SDL2IMAGE_INSTALL) install( TARGETS SDL2_image EXPORT SDL2ImageExports ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT library RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT library ) install( FILES "${CMAKE_CURRENT_SOURCE_DIR}/include/SDL_image.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL2" COMPONENT DEVEL ) if(INSTALL_EXTRA_TARGETS) install( TARGETS ${INSTALL_EXTRA_TARGETS} EXPORT SDL2ImageExports ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT library RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT library PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" COMPONENT devel ) endif() if(WIN32 AND NOT MINGW) set(SDLIMAGE_INSTALL_CMAKEDIR_DEFAULT "cmake") else() set(SDLIMAGE_INSTALL_CMAKEDIR_DEFAULT "${CMAKE_INSTALL_LIBDIR}/cmake/SDL2_image") endif() set(SDLIMAGE_INSTALL_CMAKEDIR "${SDLIMAGE_INSTALL_CMAKEDIR_DEFAULT}" CACHE STRING "Location where to install SDL2_imageConfig.cmake") configure_package_config_file(SDL2_imageConfig.cmake.in SDL2_imageConfig.cmake INSTALL_DESTINATION "${SDLIMAGE_INSTALL_CMAKEDIR}" ) write_basic_package_version_file("${PROJECT_BINARY_DIR}/SDL2_imageConfigVersion.cmake" VERSION ${FULL_VERSION} COMPATIBILITY AnyNewerVersion ) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/SDL2_imageConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/SDL2_imageConfigVersion.cmake" cmake/Findlibjxl.cmake cmake/Findwebp.cmake DESTINATION "${SDLIMAGE_INSTALL_CMAKEDIR}" COMPONENT devel ) install(EXPORT SDL2ImageExports FILE SDL2_image-${sdl2_image_install_name_infix}-targets.cmake NAMESPACE SDL2_image:: DESTINATION "${SDLIMAGE_INSTALL_CMAKEDIR}" COMPONENT devel ) set(prefix "${CMAKE_INSTALL_PREFIX}") set(exec_prefix "\${prefix}") set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") set(includedir "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") set(PACKAGE "${PROJECT_NAME}") set(VERSION "${FULL_VERSION}") set(SDL_VERSION "${SDL_REQUIRED_VERSION}") string(JOIN " " PC_REQUIRES ${PC_REQUIRES}) string(JOIN " " PC_LIBS ${PC_LIBS}) configure_file("${PROJECT_SOURCE_DIR}/SDL2_image.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/SDL2_image.pc.intermediate" @ONLY) file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/SDL2_image-$.pc" INPUT "${CMAKE_CURRENT_BINARY_DIR}/SDL2_image.pc.intermediate") set(PC_DESTDIR) if(CMAKE_SYSTEM_NAME MATCHES FreeBSD) # FreeBSD uses ${PREFIX}/libdata/pkgconfig set(PC_DESTDIR "libdata/pkgconfig") else() set(PC_DESTDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig") endif() # Always install SDL2_net.pc file: libraries might be different between config modes install(CODE " # FIXME: use file(COPY_FILE) if minimum CMake version >= 3.21 execute_process(COMMAND \"\${CMAKE_COMMAND}\" -E copy_if_different \"${CMAKE_CURRENT_BINARY_DIR}/SDL2_image-$.pc\" \"${CMAKE_CURRENT_BINARY_DIR}/SDL2_image.pc\") file(INSTALL DESTINATION \"\${CMAKE_INSTALL_PREFIX}/${PC_DESTDIR}\" TYPE FILE FILES \"${CMAKE_CURRENT_BINARY_DIR}/SDL2_image.pc\")" COMPONENT devel) if(SDL2IMAGE_BUILD_SHARED_LIBS AND (APPLE OR (UNIX AND NOT ANDROID))) install( FILES "${PROJECT_BINARY_DIR}/libSDL2_image$<$:${SDL2IMAGE_DEBUG_POSTFIX}>$" DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel ) endif() install(FILES "LICENSE.txt" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/licenses/${PROJECT_NAME}" COMPONENT library ) endif() if(SDL2IMAGE_SAMPLES) add_executable(showanim examples/showanim.c) add_executable(showimage examples/showimage.c) find_package(SDL2main) foreach(prog showanim showimage) # FIXME: mingw should be handled by SDL2::SDL2(-static) target if(MINGW) target_link_libraries(${prog} PRIVATE mingw32) target_link_options(${prog} PRIVATE -mwindows) endif() target_link_libraries(${prog} PRIVATE SDL2_image::${sdl2_image_export_name}) if(TARGET SDL2::SDL2main) target_link_libraries(${prog} PRIVATE SDL2::SDL2main) endif() target_link_libraries(${prog} PRIVATE ${sdl2_target_name}) if(SDL2IMAGE_SAMPLES_INSTALL) install(TARGETS ${prog} RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ) endif() endforeach() endif() add_library(SDL2::image INTERFACE IMPORTED GLOBAL) set_target_properties(SDL2::image PROPERTIES INTERFACE_LINK_LIBRARIES "SDL2_image" ) if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.17") set_target_properties(SDL2::image PROPERTIES DEPRECATION "Use SDL2_image::SDL2_image or SDL2_image::SDL2_image-static instead" ) endif() if(SDL2IMAGE_TESTS) enable_testing() add_subdirectory(test) endif() set(available_deps) set(unavailable_deps) foreach(dep IN LISTS SDL2IMAGE_BACKENDS) set(var SDL2IMAGE_${dep}_ENABLED) if(NOT DEFINED ${var}) message(AUTHOR_WARNING "${var} not defined") endif() if(${var}) list(APPEND available_deps ${dep}) else() list(APPEND unavailable_deps ${dep}) endif() endforeach() string(JOIN " " avail_str ${available_deps}) string(TOLOWER "${avail_str}" avail_str) string(JOIN " " unavail_str ${unavailable_deps}) string(TOLOWER "${unavail_str}" unavail_str) message(STATUS "SDL2_image backends:") message(STATUS "- enabled: ${avail_str}") message(STATUS "- disabled: ${unavail_str}") SDL2_image-2.8.8/LICENSE.txt0000664000000000000000000000155614751445211012165 0ustar00Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. SDL2_image-2.8.8/Makefile.am0000664000000000000000000000563114751444612012400 0ustar00# Makefile.am for the SDL sample image loading library and viewer ACLOCAL_AMFLAGS = -I acinclude lib_LTLIBRARIES = libSDL2_image.la libSDL2_imageincludedir = $(includedir)/SDL2 libSDL2_imageinclude_HEADERS = \ include/SDL_image.h if USE_IMAGEIO IMAGEIO_SOURCE = src/IMG_ImageIO.m endif libSDL2_image_la_SOURCES = \ src/IMG.c \ src/IMG_avif.c \ src/IMG_bmp.c \ src/IMG_gif.c \ src/IMG_jpg.c \ src/IMG_jxl.c \ src/IMG_lbm.c \ src/IMG_pcx.c \ src/IMG_png.c \ src/IMG_pnm.c \ src/IMG_qoi.c \ src/IMG_svg.c \ src/IMG_stb.c \ src/IMG_tga.c \ src/IMG_tif.c \ src/IMG_xcf.c \ src/IMG_xpm.c \ src/IMG_xv.c \ src/IMG_webp.c \ src/IMG_WIC.c \ $(IMAGEIO_SOURCE) \ src/miniz.h \ src/nanosvg.h \ src/nanosvgrast.h \ src/qoi.h \ src/stb_image.h \ src/tiny_jpeg.h EXTRA_DIST = \ .gitmodules \ Android.mk \ CHANGES.txt \ CMakeLists.txt \ src/IMG_ImageIO.m \ src/IMG_xxx.c \ LICENSE.txt \ Makefile.os2 \ docs/README-emscripten.txt \ README.txt \ SDL2_image.spec \ SDL2_imageConfig.cmake.in \ VisualC \ VisualC-WinRT \ Xcode \ autogen.sh \ cmake \ external \ mingw \ src/version.rc libSDL2_image_la_CPPFLAGS = -I$(srcdir)/include libSDL2_image_la_LDFLAGS = \ -no-undefined \ -release $(LT_RELEASE) \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) $(LT_EXTRA) libSDL2_image_la_LIBADD = $(IMG_LIBS) if USE_VERSION_RC libSDL2_image_la_DEPENDENCIES = src/version.o endif pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = SDL2_image.pc .rc.o: $(RC) $< $@ noinst_PROGRAMS = showimage showanim showimage_SOURCES = examples/showimage.c showimage_CPPFLAGS = -I$(srcdir)/include showimage_LDADD = libSDL2_image.la showanim_SOURCES = examples/showanim.c showanim_CPPFLAGS = -I$(srcdir)/include showanim_LDADD = libSDL2_image.la SUBDIRS = . if BUILD_TESTS SUBDIRS += test endif # Rule to build tar-gzipped distribution package $(PACKAGE)-$(VERSION).tar.gz: distcheck # Rule to build RPM distribution package rpm: $(PACKAGE)-$(VERSION).tar.gz rpmbuild -ta $(PACKAGE)-$(VERSION).tar.gz dist-hook: ls $(distdir)/external | $(FGREP) -v download.sh | $(FGREP) -v Get-GitModules.ps1 | while read dir; do rm -rf "$(distdir)/external/$$dir"; done install-data-local: $(MKDIR_P) $(DESTDIR)$(libdir)/cmake/SDL2_image $(INSTALL) -m 644 sdl2_image-config.cmake $(DESTDIR)$(libdir)/cmake/SDL2_image $(INSTALL) -m 644 sdl2_image-config-version.cmake $(DESTDIR)$(libdir)/cmake/SDL2_image $(INSTALL) -m 644 $(srcdir)/cmake/Findlibjxl.cmake $(DESTDIR)$(libdir)/cmake/SDL2_image $(INSTALL) -m 644 $(srcdir)/cmake/Findwebp.cmake $(DESTDIR)$(libdir)/cmake/SDL2_image uninstall-hook: rm $(DESTDIR)$(libdir)/cmake/SDL2_image/sdl2_image-config.cmake rm $(DESTDIR)$(libdir)/cmake/SDL2_image/sdl2_image-config-version.cmake rm $(DESTDIR)$(libdir)/cmake/SDL2_image/Findlibjxl.cmake rm $(DESTDIR)$(libdir)/cmake/SDL2_image/Findwebp.cmake rm -r $(DESTDIR)$(libdir)/cmake/SDL2_image SDL2_image-2.8.8/Makefile.os20000664000000000000000000000651014761421756012510 0ustar00# Open Watcom makefile to build SDL2img.dll for OS/2 # wmake -f Makefile.os2 # # Remember to edit DEPS_INC and DEPS_LIB below to meet # your own environment!. LIBNAME = SDL2img MAJOR_VERSION = 2 MINOR_VERSION = 8 MICRO_VERSION = 8 VERSION = $(MAJOR_VERSION).$(MINOR_VERSION).$(MICRO_VERSION) TITLENAME = $(LIBNAME) $(VERSION) LIBFILE = $(LIBNAME).lib DLLFILE = $(LIBNAME).dll LNKFILE = $(LIBNAME).lnk # change DEPS_INC in order to point to the dependency headers. DEPS_INC=-IC:\SDL2DEV\h\SDL2 -IC:\SDL2DEV\h # change DEPS_LIB in order to point to the dependency libraries. DEPS_LIB=C:\SDL2DEV\lib SRCS = IMG.c IMG_bmp.c IMG_gif.c IMG_jpg.c IMG_lbm.c IMG_pcx.c IMG_png.c & IMG_pnm.c IMG_svg.c IMG_tga.c IMG_tif.c IMG_xcf.c IMG_xpm.c IMG_xv.c & IMG_webp.c IMG_qoi.c IMG_avif.c IMG_jxl.c IMG_stb.c LIBS = libpng.lib libtiff.lib zlib.lib jpeg.lib webpdec.lib webpdemux.lib SDL2.lib CFLAGS_BASE = -bt=os2 -ei -d0 -q -5s -fp5 -fpi87 -sg -oeatxh # warnings: CFLAGS_BASE+= -wx -wcd=202 # newer OpenWatcom versions enable W303 by default CFLAGS_BASE+= -wcd=303 # include paths: CFLAGS_BASE+= -I"$(%WATCOM)/h/os2" -I"$(%WATCOM)/h" CFLAGS_BASE+= -Iinclude $(DEPS_INC) CFLAGS =$(CFLAGS_BASE) # to build a dll: CFLAGS+= -bd # for DECLSPEC: CFLAGS+= -DBUILD_SDL # wanted formats: CFLAGS+= -DLOAD_JPG -DLOAD_PNG -DLOAD_BMP -DLOAD_GIF -DLOAD_LBM & -DLOAD_PCX -DLOAD_PNM -DLOAD_TGA -DLOAD_XCF -DLOAD_XPM & -DLOAD_XV -DLOAD_SVG -DLOAD_TIF -DLOAD_WEBP -DLOAD_QOI #CFLAGS+= -DLOAD_AVIF #CFLAGS+= -DLOAD_JXL #CFLAGS+= -DUSE_STBIMAGE -DSTBI_NO_SIMD CFLAGS+= -DSDL_IMAGE_SAVE_JPG=1 CFLAGS+= -DSDL_IMAGE_SAVE_PNG=1 CFLAGS+= -DSDL_BUILD_MAJOR_VERSION=$(MAJOR_VERSION) CFLAGS+= -DSDL_BUILD_MINOR_VERSION=$(MINOR_VERSION) CFLAGS+= -DSDL_BUILD_MICRO_VERSION=$(MICRO_VERSION) .extensions: .extensions: .lib .dll .obj .c OBJS = $(SRCS:.c=.obj) all: $(LIBFILE) showimage.exe showanim.exe $(LIBFILE): $(DLLFILE) @echo * Create library: $@... wlib -b -n -q -c -pa -s -t -zld -ii -io $@ $(DLLFILE) $(DLLFILE): $(OBJS) $(LNKFILE) @echo * Link: $@ wlink @$(LNKFILE) $(LNKFILE): @%create $@ @%append $@ SYSTEM os2v2_dll INITINSTANCE TERMINSTANCE @%append $@ NAME $(LIBNAME) @for %i in ($(OBJS)) do @%append $@ FILE %i @%append $@ OPTION QUIET @%append $@ OPTION DESCRIPTION '@$#libsdl org:$(VERSION)$#@Simple DirectMedia Layer Image Library' @%append $@ LIBPATH $(DEPS_LIB) @for %i in ($(LIBS)) do @%append $@ LIB %i @%append $@ OPTION MAP=$* @%append $@ OPTION ELIMINATE @%append $@ OPTION MANYAUTODATA @%append $@ OPTION OSNAME='OS/2 and eComStation' @%append $@ OPTION SHOWDEAD .c: ./src .c.obj: wcc386 $(CFLAGS) -fo=$^@ $< showimage.obj: examples/showimage.c wcc386 $(CFLAGS_BASE) -fo=$^@ $< showanim.obj: examples/showanim.c wcc386 $(CFLAGS_BASE) -fo=$^@ $< showimage.exe: $(LIBFILE) showimage.obj wlink SYS os2v2 OP q LIBPATH $(DEPS_LIB) LIBR {$(LIBFILE) SDL2.lib} F {showimage.obj} N showimage.exe showanim.exe: $(LIBFILE) showanim.obj wlink SYS os2v2 OP q LIBPATH $(DEPS_LIB) LIBR {$(LIBFILE) SDL2.lib} F {showanim.obj} N showanim.exe clean: .SYMBOLIC @echo * Clean: $(TITLENAME) @if exist *.obj rm *.obj @if exist *.err rm *.err @if exist $(LNKFILE) rm $(LNKFILE) distclean: .SYMBOLIC clean @if exist $(DLLFILE) rm $(DLLFILE) @if exist $(LIBFILE) rm $(LIBFILE) @if exist *.exe rm *.exe @if exist *.map rm *.map SDL2_image-2.8.8/README.txt0000664000000000000000000000377114444663624012052 0ustar00 SDL_image 2.0 The latest version of this library is available from GitHub: https://github.com/libsdl-org/SDL_image/releases This is a simple library to load images of various formats as SDL surfaces. It can load BMP, GIF, JPEG, LBM, PCX, PNG, PNM (PPM/PGM/PBM), QOI, TGA, XCF, XPM, and simple SVG format images. It can also load AVIF, JPEG-XL, TIFF, and WebP images, depending on build options (see the note below for details.) API: #include "SDL_image.h" SDL_Surface *IMG_Load(const char *file); or SDL_Surface *IMG_Load_RW(SDL_RWops *src, int freesrc); or SDL_Surface *IMG_LoadTyped_RW(SDL_RWops *src, int freesrc, char *type); where type is a string specifying the format (i.e. "PNG" or "pcx"). Note that IMG_Load_RW cannot load TGA images. To create a surface from an XPM image included in C source, use: SDL_Surface *IMG_ReadXPMFromArray(char **xpm); An example program 'showimage' is included, with source in 'examples/showimage.c' Documentation is also available online at https://wiki.libsdl.org/SDL2_image This library is under the zlib License, see the file "LICENSE.txt" for details. Note: Support for AVIF, JPEG-XL, TIFF, and WebP are not included by default because of the size of the decode libraries, but you can get them by running external/download.sh - When building with CMake, you can enable the appropriate SDL2IMAGE_* options defined in CMakeLists.txt. SDL2IMAGE_VENDORED allows switching between system and vendored libraries. - When building with configure/make, you can build and install them normally and the configure script will detect and use them. - When building with Visual Studio, you will need to build the libraries and then add the appropriate LOAD_* preprocessor define to the Visual Studio project. - When building with Xcode, you can edit the config at the top of the project to enable them, and you will need to include the appropriate framework in your application. - For Android, you can edit the config at the top of Android.mk to enable them. SDL2_image-2.8.8/SDL2_image.pc.in0000664000000000000000000000051314167362225013135 0ustar00prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: @PACKAGE@ Description: image loading library for Simple DirectMedia Layer Version: @VERSION@ Requires: sdl2 >= @SDL_VERSION@ Libs: -L${libdir} -lSDL2_image Requires.private: @PC_REQUIRES@ Libs.private: @PC_LIBS@ Cflags: -I${includedir}/SDL2 SDL2_image-2.8.8/SDL2_image.spec.in0000664000000000000000000000310614030145740013454 0ustar00%define name @PACKAGE@ %define version @VERSION@ %define release 1 Summary: Simple DirectMedia Layer - Sample Image Loading Library Name: %{name} Version: %{version} Release: %{release} Source0: %{name}-%{version}.tar.gz License: LGPL Group: System Environment/Libraries BuildRoot: /var/tmp/%{name}-buildroot Prefix: %{_prefix} Packager: Hakan Tandogan #BuildRequires: SDL2-devel #BuildRequires: libjpeg-devel #BuildRequires: libpng-devel #BuildRequires: libtiff-devel %description This is a simple library to load images of various formats as SDL surfaces. This library supports BMP, PPM, PCX, GIF, JPEG, PNG, and TIFF formats. %package devel Summary: Libraries, includes and more to develop SDL applications. Group: Development/Libraries Requires: %{name} Requires: SDL2-devel %description devel This is a simple library to load images of various formats as SDL surfaces. This library supports BMP, PPM, PCX, GIF, JPEG, PNG, and TIFF formats. %prep rm -rf ${RPM_BUILD_ROOT} %setup %build CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{prefix} make %install rm -rf $RPM_BUILD_ROOT make install prefix=$RPM_BUILD_ROOT/%{prefix} %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root) %doc README.txt CHANGES.txt LICENSE.txt %{prefix}/lib/lib*.so.* %files devel %defattr(-,root,root) %{prefix}/lib/lib*.a %{prefix}/lib/lib*.la %{prefix}/lib/lib*.so %{prefix}/include/*/ %{prefix}/lib/pkgconfig/*.pc %changelog * Wed Jan 19 2000 Sam Lantinga - converted to get package information from configure * Tue Jan 18 2000 Hakan Tandogan - initial spec file SDL2_image-2.8.8/SDL2_imageConfig.cmake.in0000664000000000000000000000621414723631750014745 0ustar00# sdl2_image cmake project-config input for CMakeLists.txt script include(FeatureSummary) set_package_properties(SDL2_image PROPERTIES URL "https://www.libsdl.org/projects/SDL_image/" DESCRIPTION "SDL_image is an image file loading library" ) set(SDL2_image_FOUND ON) set(SDL2IMAGE_AVIF @SDL2IMAGE_AVIF@) set(SDL2IMAGE_BMP @SDL2IMAGE_BMP@) set(SDL2IMAGE_GIF @SDL2IMAGE_GIF@) set(SDL2IMAGE_JPG @SDL2IMAGE_JPG@) set(SDL2IMAGE_JXL @SDL2IMAGE_JXL@) set(SDL2IMAGE_LBM @SDL2IMAGE_LBM@) set(SDL2IMAGE_PCX @SDL2IMAGE_PCX@) set(SDL2IMAGE_PNG @SDL2IMAGE_PNG@) set(SDL2IMAGE_PNM @SDL2IMAGE_PNM@) set(SDL2IMAGE_QOI @SDL2IMAGE_QOI@) set(SDL2IMAGE_SVG @SDL2IMAGE_SVG@) set(SDL2IMAGE_TGA @SDL2IMAGE_TGA@) set(SDL2IMAGE_TIF @SDL2IMAGE_TIF@) set(SDL2IMAGE_XCF @SDL2IMAGE_XCF@) set(SDL2IMAGE_XPM @SDL2IMAGE_XPM@) set(SDL2IMAGE_XV @SDL2IMAGE_XV@) set(SDL2IMAGE_WEBP @SDL2IMAGE_WEBP@) set(SDL2IMAGE_JPG_SAVE @SDL2IMAGE_JPG_SAVE@) set(SDL2IMAGE_PNG_SAVE @SDL2IMAGE_PNG_SAVE@) set(SDL2IMAGE_VENDORED @SDL2IMAGE_VENDORED@) set(SDL2IMAGE_BACKEND_IMAGEIO @SDL2IMAGE_BACKEND_IMAGEIO@) set(SDL2IMAGE_BACKEND_STB @SDL2IMAGE_BACKEND_STB@) set(SDL2IMAGE_BACKEND_WIC @SDL2IMAGE_BACKEND_WIC@) set(SDL2IMAGE_SDL2_REQUIRED_VERSION @SDL_REQUIRED_VERSION@) if(NOT SDL2IMAGE_VENDORED) set(_sdl_cmake_module_path "${CMAKE_MODULE_PATH}") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") endif() include(CMakeFindDependencyMacro) if(SDL2IMAGE_AVIF AND NOT SDL2IMAGE_VENDORED AND NOT TARGET avif) find_package(libavif 1.0 QUIET) if(NOT libavif_FOUND) find_package(libavif @LIBAVIF_MINIMUM_VERSION@ QUIET) endif() if(NOT libavif_FOUND) set(SDL2_image_FOUND FALSE) return() endif() endif() if(SDL2IMAGE_JPG AND NOT SDL2IMAGE_VENDORED AND NOT TARGET JPEG::JPEG) find_dependency(JPEG) endif() if(SDL2IMAGE_JXL AND NOT SDL2IMAGE_VENDORED AND NOT TARGET libjxl::libjxl) list(APPEND libjxl_ROOT "${CMAKE_CURRENT_LIST_DIR}") find_dependency(libjxl) endif() if(SDL2IMAGE_PNG AND NOT SDL2IMAGE_VENDORED AND NOT TARGET PNG::PNG) find_dependency(PNG) endif() if(SDL2IMAGE_TIF AND NOT SDL2IMAGE_VENDORED AND NOT TARGET TIFF::TIFF) find_dependency(TIFF) endif() if(SDL2IMAGE_WEBP AND NOT SDL2IMAGE_VENDORED AND NOT TARGET WebP::webp) list(APPEND webp_ROOT "${CMAKE_CURRENT_LIST_DIR}") find_dependency(webp) endif() #FIXME: can't add SDL2IMAGE_SDL2_REQUIRED_VERSION since not all SDL2 installs ship SDL2ConfigVersion.cmake if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/SDL2_image-shared-targets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/SDL2_image-shared-targets.cmake") endif() if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/SDL2_image-static-targets.cmake") include(CheckLanguage) check_language(CXX) if(SDL2IMAGE_VENDORED AND NOT CMAKE_CXX_COMPILER AND NOT _sdl2image_nowarning) message(WARNING "CXX language not enabled. Linking to SDL2_image::SDL2_image-static might fail.") endif() include("${CMAKE_CURRENT_LIST_DIR}/SDL2_image-static-targets.cmake") endif() if(NOT SDL2IMAGE_VENDORED) set(CMAKE_MODULE_PATH "${_sdl_cmake_module_path}") unset(_sdl_cmake_module_path) endif() SDL2_image-2.8.8/acinclude/ax_compute_relative_paths.m40000664000000000000000000001605414272301614017764 0ustar00# ============================================================================== # https://www.gnu.org/software/autoconf-archive/ax_compute_relative_paths.html # ============================================================================== # # SYNOPSIS # # AX_COMPUTE_RELATIVE_PATHS(PATH_LIST) # # DESCRIPTION # # PATH_LIST is a space-separated list of colon-separated triplets of the # form 'FROM:TO:RESULT'. This function iterates over these triplets and # set $RESULT to the relative path from $FROM to $TO. Note that $FROM and # $TO needs to be absolute filenames for this macro to success. # # For instance, # # first=/usr/local/bin # second=/usr/local/share # AX_COMPUTE_RELATIVE_PATHS([first:second:fs second:first:sf]) # # $fs is set to ../share # # $sf is set to ../bin # # $FROM and $TO are both eval'ed recursively and normalized, this means # that you can call this macro with autoconf's dirnames like `prefix' or # `datadir'. For example: # # AX_COMPUTE_RELATIVE_PATHS([bindir:datadir:bin_to_data]) # # AX_COMPUTE_RELATIVE_PATHS should also works with DOS filenames. # # You may want to use this macro in order to make your package # relocatable. Instead of hardcoding $datadir into your programs just # encode $bin_to_data and try to determine $bindir at run-time. # # This macro requires AX_NORMALIZE_PATH and AX_RECURSIVE_EVAL. # # LICENSE # # Copyright (c) 2008 Alexandre Duret-Lutz # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 12 AU_ALIAS([ADL_COMPUTE_RELATIVE_PATHS], [AX_COMPUTE_RELATIVE_PATHS]) AC_DEFUN([AX_COMPUTE_RELATIVE_PATHS], [for _lcl_i in $1; do _lcl_from=\[$]`echo "[$]_lcl_i" | sed 's,:.*$,,'` _lcl_to=\[$]`echo "[$]_lcl_i" | sed 's,^[[^:]]*:,,' | sed 's,:[[^:]]*$,,'` _lcl_result_var=`echo "[$]_lcl_i" | sed 's,^.*:,,'` AX_RECURSIVE_EVAL([[$]_lcl_from], [_lcl_from]) AX_RECURSIVE_EVAL([[$]_lcl_to], [_lcl_to]) _lcl_notation="$_lcl_from$_lcl_to" AX_NORMALIZE_PATH([_lcl_from],['/']) AX_NORMALIZE_PATH([_lcl_to],['/']) AX_COMPUTE_RELATIVE_PATH([_lcl_from], [_lcl_to], [_lcl_result_tmp]) AX_NORMALIZE_PATH([_lcl_result_tmp],["[$]_lcl_notation"]) eval $_lcl_result_var='[$]_lcl_result_tmp' done]) ## Note: ## ***** ## The following helper macros are too fragile to be used out ## of AX_COMPUTE_RELATIVE_PATHS (mainly because they assume that ## paths are normalized), that's why I'm keeping them in the same file. ## Still, some of them maybe worth to reuse. dnl AX_COMPUTE_RELATIVE_PATH(FROM, TO, RESULT) dnl =========================================== dnl Compute the relative path to go from $FROM to $TO and set the value dnl of $RESULT to that value. This function work on raw filenames dnl (for instead it will considerate /usr//local and /usr/local as dnl two distinct paths), you should really use AX_COMPUTE_RELATIVE_PATHS dnl instead to have the paths sanitized automatically. dnl dnl For instance: dnl first_dir=/somewhere/on/my/disk/bin dnl second_dir=/somewhere/on/another/disk/share dnl AX_COMPUTE_RELATIVE_PATH(first_dir, second_dir, first_to_second) dnl will set $first_to_second to '../../../another/disk/share'. AC_DEFUN([AX_COMPUTE_RELATIVE_PATH], [AX_COMPUTE_COMMON_PATH([$1], [$2], [_lcl_common_prefix]) AX_COMPUTE_BACK_PATH([$1], [_lcl_common_prefix], [_lcl_first_rel]) AX_COMPUTE_SUFFIX_PATH([$2], [_lcl_common_prefix], [_lcl_second_suffix]) $3="[$]_lcl_first_rel[$]_lcl_second_suffix"]) dnl AX_COMPUTE_COMMON_PATH(LEFT, RIGHT, RESULT) dnl ============================================ dnl Compute the common path to $LEFT and $RIGHT and set the result to $RESULT. dnl dnl For instance: dnl first_path=/somewhere/on/my/disk/bin dnl second_path=/somewhere/on/another/disk/share dnl AX_COMPUTE_COMMON_PATH(first_path, second_path, common_path) dnl will set $common_path to '/somewhere/on'. AC_DEFUN([AX_COMPUTE_COMMON_PATH], [$3='' _lcl_second_prefix_match='' while test "[$]_lcl_second_prefix_match" != 0; do _lcl_first_prefix=`expr "x[$]$1" : "x\([$]$3/*[[^/]]*\)"` _lcl_second_prefix_match=`expr "x[$]$2" : "x[$]_lcl_first_prefix"` if test "[$]_lcl_second_prefix_match" != 0; then if test "[$]_lcl_first_prefix" != "[$]$3"; then $3="[$]_lcl_first_prefix" else _lcl_second_prefix_match=0 fi fi done]) dnl AX_COMPUTE_SUFFIX_PATH(PATH, SUBPATH, RESULT) dnl ============================================== dnl Subtract $SUBPATH from $PATH, and set the resulting suffix dnl (or the empty string if $SUBPATH is not a subpath of $PATH) dnl to $RESULT. dnl dnl For instance: dnl first_path=/somewhere/on/my/disk/bin dnl second_path=/somewhere/on dnl AX_COMPUTE_SUFFIX_PATH(first_path, second_path, common_path) dnl will set $common_path to '/my/disk/bin'. AC_DEFUN([AX_COMPUTE_SUFFIX_PATH], [$3=`expr "x[$]$1" : "x[$]$2/*\(.*\)"`]) dnl AX_COMPUTE_BACK_PATH(PATH, SUBPATH, RESULT) dnl ============================================ dnl Compute the relative path to go from $PATH to $SUBPATH, knowing that dnl $SUBPATH is a subpath of $PATH (any other words, only repeated '../' dnl should be needed to move from $PATH to $SUBPATH) and set the value dnl of $RESULT to that value. If $SUBPATH is not a subpath of PATH, dnl set $RESULT to the empty string. dnl dnl For instance: dnl first_path=/somewhere/on/my/disk/bin dnl second_path=/somewhere/on dnl AX_COMPUTE_BACK_PATH(first_path, second_path, back_path) dnl will set $back_path to '../../../'. AC_DEFUN([AX_COMPUTE_BACK_PATH], [AX_COMPUTE_SUFFIX_PATH([$1], [$2], [_lcl_first_suffix]) $3='' _lcl_tmp='xxx' while test "[$]_lcl_tmp" != ''; do _lcl_tmp=`expr "x[$]_lcl_first_suffix" : "x[[^/]]*/*\(.*\)"` if test "[$]_lcl_first_suffix" != ''; then _lcl_first_suffix="[$]_lcl_tmp" $3="../[$]$3" fi done]) SDL2_image-2.8.8/acinclude/ax_normalize_path.m40000664000000000000000000001134214272301614016225 0ustar00# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_normalize_path.html # =========================================================================== # # SYNOPSIS # # AX_NORMALIZE_PATH(VARNAME, [REFERENCE_STRING]) # # DESCRIPTION # # Perform some cleanups on the value of $VARNAME (interpreted as a path): # # - empty paths are changed to '.' # - trailing slashes are removed # - repeated slashes are squeezed except a leading doubled slash '//' # (which might indicate a networked disk on some OS). # # REFERENCE_STRING is used to turn '/' into '\' and vice-versa: if # REFERENCE_STRING contains some backslashes, all slashes and backslashes # are turned into backslashes, otherwise they are all turned into slashes. # # This makes processing of DOS filenames quite easier, because you can # turn a filename to the Unix notation, make your processing, and turn it # back to original notation. # # filename='A:\FOO\\BAR\' # old_filename="$filename" # # Switch to the unix notation # AX_NORMALIZE_PATH([filename], ["/"]) # # now we have $filename = 'A:/FOO/BAR' and we can process it as if # # it was a Unix path. For instance let's say that you want # # to append '/subpath': # filename="$filename/subpath" # # finally switch back to the original notation # AX_NORMALIZE_PATH([filename], ["$old_filename"]) # # now $filename equals to 'A:\FOO\BAR\subpath' # # One good reason to make all path processing with the unix convention is # that backslashes have a special meaning in many cases. For instance # # expr 'A:\FOO' : 'A:\Foo' # # will return 0 because the second argument is a regex in which # backslashes have to be backslashed. In other words, to have the two # strings to match you should write this instead: # # expr 'A:\Foo' : 'A:\\Foo' # # Such behavior makes DOS filenames extremely unpleasant to work with. So # temporary turn your paths to the Unix notation, and revert them to the # original notation after the processing. See the macro # AX_COMPUTE_RELATIVE_PATHS for a concrete example of this. # # REFERENCE_STRING defaults to $VARIABLE, this means that slashes will be # converted to backslashes if $VARIABLE already contains some backslashes # (see $thirddir below). # # firstdir='/usr/local//share' # seconddir='C:\Program Files\\' # thirddir='C:\home/usr/' # AX_NORMALIZE_PATH([firstdir]) # AX_NORMALIZE_PATH([seconddir]) # AX_NORMALIZE_PATH([thirddir]) # # $firstdir = '/usr/local/share' # # $seconddir = 'C:\Program Files' # # $thirddir = 'C:\home\usr' # # LICENSE # # Copyright (c) 2008 Alexandre Duret-Lutz # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 8 AU_ALIAS([ADL_NORMALIZE_PATH], [AX_NORMALIZE_PATH]) AC_DEFUN([AX_NORMALIZE_PATH], [case ":[$]$1:" in # change empty paths to '.' ::) $1='.' ;; # strip trailing slashes :*[[\\/]]:) $1=`echo "[$]$1" | sed 's,[[\\/]]*[$],,'` ;; :*:) ;; esac # squeeze repeated slashes case ifelse($2,,"[$]$1",$2) in # if the path contains any backslashes, turn slashes into backslashes *\\*) $1=`echo "[$]$1" | sed 's,\(.\)[[\\/]][[\\/]]*,\1\\\\,g'` ;; # if the path contains slashes, also turn backslashes into slashes *) $1=`echo "[$]$1" | sed 's,\(.\)[[\\/]][[\\/]]*,\1/,g'` ;; esac]) SDL2_image-2.8.8/acinclude/ax_recursive_eval.m40000664000000000000000000000455014272301614016232 0ustar00# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_recursive_eval.html # =========================================================================== # # SYNOPSIS # # AX_RECURSIVE_EVAL(VALUE, RESULT) # # DESCRIPTION # # Interpolate the VALUE in loop until it doesn't change, and set the # result to $RESULT. WARNING: It's easy to get an infinite loop with some # unsane input. # # LICENSE # # Copyright (c) 2008 Alexandre Duret-Lutz # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 1 AC_DEFUN([AX_RECURSIVE_EVAL], [_lcl_receval="$1" $2=`(test "x$prefix" = xNONE && prefix="$ac_default_prefix" test "x$exec_prefix" = xNONE && exec_prefix="${prefix}" _lcl_receval_old='' while test "[$]_lcl_receval_old" != "[$]_lcl_receval"; do _lcl_receval_old="[$]_lcl_receval" eval _lcl_receval="\"[$]_lcl_receval\"" done echo "[$]_lcl_receval")`]) SDL2_image-2.8.8/acinclude/pkg.m40000664000000000000000000002400714020407662013305 0ustar00# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 12 (pkg-config-0.29.2) dnl Copyright ТЉ 2004 Scott James Remnant . dnl Copyright ТЉ 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.2]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR SDL2_image-2.8.8/acinclude/sdl2.m40000664000000000000000000001471414727102423013375 0ustar00# Configure paths for SDL # Sam Lantinga 9/21/99 # stolen from Manish Singh # stolen back from Frank Belew # stolen from Manish Singh # Shamelessly stolen from Owen Taylor # serial 2 dnl AM_PATH_SDL2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS dnl AC_DEFUN([AM_PATH_SDL2], [dnl dnl Get the cflags and libraries from the sdl2-config script dnl AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], sdl_prefix="$withval", sdl_prefix="") AC_ARG_WITH(sdl-exec-prefix,[ --with-sdl-exec-prefix=PFX Exec prefix where SDL is installed (optional)], sdl_exec_prefix="$withval", sdl_exec_prefix="") AC_ARG_ENABLE(sdltest, [ --disable-sdltest Do not try to compile and run a test SDL program], , enable_sdltest=yes) min_sdl_version=ifelse([$1], ,2.0.0,$1) if test "x$sdl_prefix$sdl_exec_prefix" = x ; then PKG_CHECK_MODULES([SDL], [sdl2 >= $min_sdl_version], [sdl_pc=yes], [sdl_pc=no]) else sdl_pc=no if test x$sdl_exec_prefix != x ; then sdl_config_args="$sdl_config_args --exec-prefix=$sdl_exec_prefix" if test x${SDL2_CONFIG+set} != xset ; then SDL2_CONFIG=$sdl_exec_prefix/bin/sdl2-config fi fi if test x$sdl_prefix != x ; then sdl_config_args="$sdl_config_args --prefix=$sdl_prefix" if test x${SDL2_CONFIG+set} != xset ; then SDL2_CONFIG=$sdl_prefix/bin/sdl2-config fi fi fi if test "x$sdl_pc" = xyes ; then no_sdl="" SDL2_CONFIG="$PKG_CONFIG sdl2" else as_save_PATH="$PATH" if test "x$prefix" != xNONE && test "$cross_compiling" != yes; then PATH="$prefix/bin:$prefix/usr/bin:$PATH" fi AC_PATH_PROG(SDL2_CONFIG, sdl2-config, no, [$PATH]) PATH="$as_save_PATH" AC_MSG_CHECKING(for SDL - version >= $min_sdl_version) no_sdl="" if test "$SDL2_CONFIG" = "no" ; then no_sdl=yes else SDL_CFLAGS=`$SDL2_CONFIG $sdl_config_args --cflags` SDL_LIBS=`$SDL2_CONFIG $sdl_config_args --libs` sdl_major_version=`$SDL2_CONFIG $sdl_config_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` sdl_minor_version=`$SDL2_CONFIG $sdl_config_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` sdl_micro_version=`$SDL2_CONFIG $sdl_config_args --version | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` if test "x$enable_sdltest" = "xyes" ; then ac_save_CFLAGS="$CFLAGS" ac_save_CXXFLAGS="$CXXFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $SDL_CFLAGS" CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" LIBS="$LIBS $SDL_LIBS" dnl dnl Now check if the installed SDL is sufficiently new. (Also sanity dnl checks the results of sdl2-config to some extent dnl rm -f conf.sdltest AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include #include "SDL.h" int main (int argc, char *argv[]) { int major, minor, micro; FILE *fp = fopen("conf.sdltest", "w"); if (fp) fclose(fp); if (sscanf("$min_sdl_version", "%d.%d.%d", &major, &minor, µ) != 3) { printf("%s, bad version string\n", "$min_sdl_version"); exit(1); } if (($sdl_major_version > major) || (($sdl_major_version == major) && ($sdl_minor_version > minor)) || (($sdl_major_version == major) && ($sdl_minor_version == minor) && ($sdl_micro_version >= micro))) { return 0; } else { printf("\n*** 'sdl2-config --version' returned %d.%d.%d, but the minimum version\n", $sdl_major_version, $sdl_minor_version, $sdl_micro_version); printf("*** of SDL required is %d.%d.%d. If sdl2-config is correct, then it is\n", major, minor, micro); printf("*** best to upgrade to the required version.\n"); printf("*** If sdl2-config was wrong, set the environment variable SDL2_CONFIG\n"); printf("*** to point to the correct copy of sdl2-config, and remove the file\n"); printf("*** config.cache before re-running configure\n"); return 1; } } ]])], [], [no_sdl=yes], [echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" CXXFLAGS="$ac_save_CXXFLAGS" LIBS="$ac_save_LIBS" fi fi if test "x$no_sdl" = x ; then AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) fi fi if test "x$no_sdl" = x ; then ifelse([$2], , :, [$2]) else if test "$SDL2_CONFIG" = "no" ; then echo "*** The sdl2-config script installed by SDL could not be found" echo "*** If SDL was installed in PREFIX, make sure PREFIX/bin is in" echo "*** your path, or set the SDL2_CONFIG environment variable to the" echo "*** full path to sdl2-config." else if test -f conf.sdltest ; then : else echo "*** Could not run SDL test program, checking why..." CFLAGS="$CFLAGS $SDL_CFLAGS" CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" LIBS="$LIBS $SDL_LIBS" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include "SDL.h" int main(int argc, char *argv[]) { return 0; } #undef main #define main K_and_R_C_main ]], [[ return 0; ]])], [ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding SDL or finding the wrong" echo "*** version of SDL. If it is not finding SDL, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" echo "*** to the installed location Also, make sure you have run ldconfig if that" echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], [ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occured. This usually means SDL was incorrectly installed" echo "*** or that you have moved SDL since it was installed. In the latter case, you" echo "*** may want to edit the sdl2-config script: $SDL2_CONFIG" ]) CFLAGS="$ac_save_CFLAGS" CXXFLAGS="$ac_save_CXXFLAGS" LIBS="$ac_save_LIBS" fi fi SDL_CFLAGS="" SDL_LIBS="" ifelse([$3], , :, [$3]) fi AC_SUBST(SDL_CFLAGS) AC_SUBST(SDL_LIBS) rm -f conf.sdltest ]) SDL2_image-2.8.8/acinclude/tar.m40000664000000000000000000001132114210747152013307 0ustar00# Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' dnl SDL: No following the symlinks: removed tar 'h' and cpio/pax '-L' switches m4_if([$1], [v7], [am__tar='$${TAR-tar} cof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test x$am_uid = xunknown; then AC_MSG_WARN([ancient id detected; assuming current UID is ok, but dist-ustar might not work]) elif test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test x$gm_gid = xunknown; then AC_MSG_WARN([ancient id detected; assuming current GID is ok, but dist-ustar might not work]) elif test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -cf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -cf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar cf - "$$tardir"' am__tar_='tar cf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -x $1 -w "$$tardir"' am__tar_='pax -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1' am__tar_='find "$tardir" -print | cpio -o -H $1' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR SDL2_image-2.8.8/autogen.sh0000775000000000000000000000030114252362742012331 0ustar00#!/bin/sh set -e "${ACLOCAL:-aclocal}" -I acinclude "${AUTOMAKE:-automake}" --foreign --include-deps --add-missing --copy "${AUTOCONF:-autoconf}" echo "Now you are ready to run ./configure" SDL2_image-2.8.8/build-scripts/android-prefab.sh0000775000000000000000000003016714322072514016336 0ustar00#!/bin/bash set -e if ! [ "x$ANDROID_NDK_HOME" != "x" -a -d "$ANDROID_NDK_HOME" ]; then echo "ANDROID_NDK_HOME environment variable is not set" exit 1 fi if ! [ "x$ANDROID_HOME" != "x" -a -d "$ANDROID_HOME" ]; then echo "ANDROID_HOME environment variable is not set" exit 1 fi if [ "x$ANDROID_API" = "x" ]; then ANDROID_API="$(ls "$ANDROID_HOME/platforms" | grep -E "^android-[0-9]+$" | sed 's/android-//' | sort -n -r | head -1)" if [ "x$ANDROID_API" = "x" ]; then echo "No Android platform found in $ANDROID_HOME/platforms" exit 1 fi else if ! [ -d "$ANDROID_HOME/platforms/android-$ANDROID_API" ]; then echo "Android api version $ANDROID_API is not available ($ANDROID_HOME/platforms/android-$ANDROID_API does not exist)" >2 exit 1 fi fi android_platformdir="$ANDROID_HOME/platforms/android-$ANDROID_API" echo "Building for android api version $ANDROID_API" echo "android_platformdir=$android_platformdir" scriptdir=$(cd -P -- "$(dirname -- "$0")" && printf '%s\n' "$(pwd -P)") sdlimage_root=$(cd -P -- "$(dirname -- "$0")/.." && printf '%s\n' "$(pwd -P)") build_root="${sdlimage_root}/build-android-prefab" android_abis="armeabi-v7a arm64-v8a x86 x86_64" android_api=19 android_ndk=21 android_stl="c++_shared" sdlimage_major=$(sed -ne 's/^#define SDL_IMAGE_MAJOR_VERSION *//p' "${sdlimage_root}/SDL_image.h") sdlimage_minor=$(sed -ne 's/^#define SDL_IMAGE_MINOR_VERSION *//p' "${sdlimage_root}/SDL_image.h") sdlimage_patch=$(sed -ne 's/^#define SDL_IMAGE_PATCHLEVEL *//p' "${sdlimage_root}/SDL_image.h") sdlimage_version="${sdlimage_major}.${sdlimage_minor}.${sdlimage_patch}" echo "Building Android prefab package for SDL_image version $sdlimage_version" if test ! -d "${sdl_build_root}"; then echo "sdl_build_root is not defined or is not a directory." echo "Set this environment folder to the root of an android SDL${sdlimage_major} prefab build" echo "This usually is SDL/build-android-prefab" exit 1 fi prefabhome="${build_root}/prefab-${sdlimage_version}" rm -rf "$prefabhome" mkdir -p "${prefabhome}" build_cmake_projects() { for android_abi in $android_abis; do rm -rf "${build_root}/build_${android_abi}/prefix" for build_shared_libs in ON OFF; do echo "Configuring CMake project for $android_abi (shared=${build_shared_libs})" cmake -S "${sdlimage_root}" -B "${build_root}/build_${android_abi}/shared_${build_shared_libs}" \ -DSDL2IMAGE_DEPS_SHARED=ON \ -DSDL2IMAGE_VENDORED=ON \ -DSDL2IMAGE_BACKEND_STB=OFF \ -DSDL2IMAGE_AVIF=OFF \ -DSDL2IMAGE_BMP=ON \ -DSDL2IMAGE_GIF=ON \ -DSDL2IMAGE_JPG=ON \ -DSDL2IMAGE_JXL=OFF \ -DSJPEG_ANDROID_NDK_PATH="${ANDROID_NDK_HOME}" \ -DSDL2IMAGE_LBM=ON \ -DSDL2IMAGE_PCX=ON \ -DSDL2IMAGE_PNG=ON \ -DSDL2IMAGE_PNM=ON \ -DSDL2IMAGE_QOI=ON \ -DSDL2IMAGE_SVG=ON \ -DSDL2IMAGE_TGA=ON \ -DSDL2IMAGE_TIF=ON \ -DSDL2IMAGE_WEBP=ON \ -DSDL2IMAGE_XCF=ON \ -DSDL2IMAGE_XPM=ON \ -DSDL2IMAGE_XV=ON \ -DCMAKE_TOOLCHAIN_FILE="$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake" \ -DSDL${sdlimage_major}_DIR="${sdl_build_root}/build_${android_abi}/prefix/lib/cmake/SDL${sdlimage_major}" \ -DANDROID_PLATFORM=${android_platform} \ -DANDROID_ABI=${android_abi} \ -DBUILD_SHARED_LIBS=${build_shared_libs} \ -DCMAKE_INSTALL_PREFIX="${build_root}/build_${android_abi}/prefix" \ -DCMAKE_INSTALL_INCLUDEDIR=include \ -DCMAKE_INSTALL_LIBDIR=lib \ -DCMAKE_BUILD_TYPE=Release \ -DSDL${sdlimage_major}IMAGE_SAMPLES=OFF \ -DSDL${sdlimage_major}IMAGE_TESTS=OFF \ -GNinja echo "Building CMake project for $android_abi (shared=${build_shared_libs})" cmake --build "${build_root}/build_${android_abi}/shared_${build_shared_libs}" echo "Installing CMake project for $android_abi (shared=${build_shared_libs})" cmake --install "${build_root}/build_${android_abi}/shared_${build_shared_libs}" done done } pom_filename="SDL${sdlimage_major}_image-${sdlimage_version}.pom" pom_filepath="${prefabhome}/${pom_filename}" create_pom_xml() { echo "Creating ${pom_filename}" cat >"${pom_filepath}" < 4.0.0 org.libsdl.android SDL${sdlimage_major}_image ${sdlimage_version} aar SDL${sdlimage_major}_image The AAR for SDL${sdlimage_major}_image https://libsdl.org/ zlib License https://github.com/libsdl-org/SDL_image/blob/main/LICENSE.txt repo Sam Lantinga slouken@libsdl.org SDL https://www.libsdl.org scm:git:https://github.com/libsdl-org/SDL_image scm:git:ssh://github.com:libsdl-org/SDL_image.git https://github.com/libsdl-org/SDL_image ossrh https://s01.oss.sonatype.org/content/repositories/snapshots ossrh https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ EOF } create_aar_androidmanifest() { echo "Creating AndroidManifest.xml" cat >"${aar_root}/AndroidManifest.xml" < EOF } echo "Creating AAR root directory" aar_root="${prefabhome}/SDL${sdlimage_major}_image-${sdlimage_version}" mkdir -p "${aar_root}" aar_metainfdir_path=${aar_root}/META-INF mkdir -p "${aar_metainfdir_path}" cp "${sdlimage_root}/LICENSE.txt" "${aar_metainfdir_path}" prefabworkdir="${aar_root}/prefab" mkdir -p "${prefabworkdir}" cat >"${prefabworkdir}/prefab.json" <"${sdl_moduleworkdir}/module.json" <"${abi_sdllibdir}/abi.json" <"${sdl_moduleworkdir}/module.json" <"${abi_sdllibdir}/abi.json" <"${sdl_moduleworkdir}/module.json" <"${abi_sdllibdir}/abi.json" <"${aar_metainfdir_path}/LICENSE.zlib.txt" create_shared_module external_libpng libpng16 ":external_zlib" cp "${sdlimage_root}/external/libpng/LICENSE" "${aar_metainfdir_path}/LICENSE.libpng.txt" create_shared_module external_libjpeg libjpeg "" cp "${sdlimage_root}/external/jpeg/README" "${aar_metainfdir_path}/LICENSE.libjpeg.txt" create_shared_module external_libtiff libtiff "" cp "${sdlimage_root}/external/libtiff/COPYRIGHT" "${aar_metainfdir_path}/LICENSE.libtiff.txt" create_shared_module external_libwebp libwebp "" cp "${sdlimage_root}/external/libwebp/COPYING" "${aar_metainfdir_path}/LICENSE.libwebp.txt" #create_shared_module libbrotlicommon libbrotlicommon "" #create_shared_module libbrotlidec libbrotlidec ":libbrotlicommon" #create_shared_module libbrotlienc libbrotlienc ":libbrotlicommon" #cp "${sdlimage_root}/external/libjxl/third_party/brotli/LICENSE" "${aar_metainfdir_path}/LICENSE.brotli.txt" #create_shared_module external_libjxl libjxl ":brotlienc :brotlidec" #cp "${sdlimage_root}/external/libjxl/LICENSE" "${aar_metainfdir_path}/LICENSE.libjxl.txt" pushd "${aar_root}" aar_filename="SDL${sdlimage_major}_image-${sdlimage_version}.aar" zip -r "${aar_filename}" AndroidManifest.xml prefab META-INF zip -Tv "${aar_filename}" 2>/dev/null ; mv "${aar_filename}" "${prefabhome}" popd maven_filename="SDL${sdlimage_major}_image-${sdlimage_version}.zip" pushd "${prefabhome}" zip_filename="SDL${sdlimage_major}_image-${sdlimage_version}.zip" zip "${maven_filename}" "${aar_filename}" "${pom_filename}" 2>/dev/null; zip -Tv "${zip_filename}" 2>/dev/null; popd echo "Prefab zip is ready at ${prefabhome}/${aar_filename}" echo "Maven archive is ready at ${prefabhome}/${zip_filename}" SDL2_image-2.8.8/build-scripts/build-release.py0000775000000000000000000022601714733551731016226 0ustar00#!/usr/bin/env python3 """ This script is shared between SDL2, SDL3, and all satellite libraries. Don't specialize this script for doing project-specific modifications. Rather, modify release-info.json. """ import argparse import collections import dataclasses from collections.abc import Callable import contextlib import datetime import fnmatch import glob import io import json import logging import multiprocessing import os from pathlib import Path import platform import re import shlex import shutil import subprocess import sys import tarfile import tempfile import textwrap import typing import zipfile logger = logging.getLogger(__name__) GIT_HASH_FILENAME = ".git-hash" REVISION_TXT = "REVISION.txt" def safe_isotime_to_datetime(str_isotime: str) -> datetime.datetime: try: return datetime.datetime.fromisoformat(str_isotime) except ValueError: pass logger.warning("Invalid iso time: %s", str_isotime) if str_isotime[-6:-5] in ("+", "-"): # Commits can have isotime with invalid timezone offset (e.g. "2021-07-04T20:01:40+32:00") modified_str_isotime = str_isotime[:-6] + "+00:00" try: return datetime.datetime.fromisoformat(modified_str_isotime) except ValueError: pass raise ValueError(f"Invalid isotime: {str_isotime}") def arc_join(*parts: list[str]) -> str: assert all(p[:1] != "/" and p[-1:] != "/" for p in parts), f"None of {parts} may start or end with '/'" return "/".join(p for p in parts if p) @dataclasses.dataclass(frozen=True) class VsArchPlatformConfig: arch: str configuration: str platform: str def extra_context(self): return { "ARCH": self.arch, "CONFIGURATION": self.configuration, "PLATFORM": self.platform, } @contextlib.contextmanager def chdir(path): original_cwd = os.getcwd() try: os.chdir(path) yield finally: os.chdir(original_cwd) class Executer: def __init__(self, root: Path, dry: bool=False): self.root = root self.dry = dry def run(self, cmd, cwd=None, env=None): logger.info("Executing args=%r", cmd) sys.stdout.flush() if not self.dry: subprocess.check_call(cmd, cwd=cwd or self.root, env=env, text=True) def check_output(self, cmd, cwd=None, dry_out=None, env=None, text=True): logger.info("Executing args=%r", cmd) sys.stdout.flush() if self.dry: return dry_out return subprocess.check_output(cmd, cwd=cwd or self.root, env=env, text=text) class SectionPrinter: @contextlib.contextmanager def group(self, title: str): print(f"{title}:") yield class GitHubSectionPrinter(SectionPrinter): def __init__(self): super().__init__() self.in_group = False @contextlib.contextmanager def group(self, title: str): print(f"::group::{title}") assert not self.in_group, "Can enter a group only once" self.in_group = True yield self.in_group = False print("::endgroup::") class VisualStudio: def __init__(self, executer: Executer, year: typing.Optional[str]=None): self.executer = executer self.vsdevcmd = self.find_vsdevcmd(year) self.msbuild = self.find_msbuild() @property def dry(self) -> bool: return self.executer.dry VS_YEAR_TO_VERSION = { "2022": 17, "2019": 16, "2017": 15, "2015": 14, "2013": 12, } def find_vsdevcmd(self, year: typing.Optional[str]=None) -> typing.Optional[Path]: vswhere_spec = ["-latest"] if year is not None: try: version = self.VS_YEAR_TO_VERSION[year] except KeyError: logger.error("Invalid Visual Studio year") return None vswhere_spec.extend(["-version", f"[{version},{version+1})"]) vswhere_cmd = ["vswhere"] + vswhere_spec + ["-property", "installationPath"] vs_install_path = Path(self.executer.check_output(vswhere_cmd, dry_out="/tmp").strip()) logger.info("VS install_path = %s", vs_install_path) assert vs_install_path.is_dir(), "VS installation path does not exist" vsdevcmd_path = vs_install_path / "Common7/Tools/vsdevcmd.bat" logger.info("vsdevcmd path = %s", vsdevcmd_path) if self.dry: vsdevcmd_path.parent.mkdir(parents=True, exist_ok=True) vsdevcmd_path.touch(exist_ok=True) assert vsdevcmd_path.is_file(), "vsdevcmd.bat batch file does not exist" return vsdevcmd_path def find_msbuild(self) -> typing.Optional[Path]: vswhere_cmd = ["vswhere", "-latest", "-requires", "Microsoft.Component.MSBuild", "-find", r"MSBuild\**\Bin\MSBuild.exe"] msbuild_path = Path(self.executer.check_output(vswhere_cmd, dry_out="/tmp/MSBuild.exe").strip()) logger.info("MSBuild path = %s", msbuild_path) if self.dry: msbuild_path.parent.mkdir(parents=True, exist_ok=True) msbuild_path.touch(exist_ok=True) assert msbuild_path.is_file(), "MSBuild.exe does not exist" return msbuild_path def build(self, arch_platform: VsArchPlatformConfig, projects: list[Path]): assert projects, "Need at least one project to build" vsdev_cmd_str = f"\"{self.vsdevcmd}\" -arch={arch_platform.arch}" msbuild_cmd_str = " && ".join([f"\"{self.msbuild}\" \"{project}\" /m /p:BuildInParallel=true /p:Platform={arch_platform.platform} /p:Configuration={arch_platform.configuration}" for project in projects]) bat_contents = f"{vsdev_cmd_str} && {msbuild_cmd_str}\n" bat_path = Path(tempfile.gettempdir()) / "cmd.bat" with bat_path.open("w") as f: f.write(bat_contents) logger.info("Running cmd.exe script (%s): %s", bat_path, bat_contents) cmd = ["cmd.exe", "/D", "/E:ON", "/V:OFF", "/S", "/C", f"CALL {str(bat_path)}"] self.executer.run(cmd) class Archiver: def __init__(self, zip_path: typing.Optional[Path]=None, tgz_path: typing.Optional[Path]=None, txz_path: typing.Optional[Path]=None): self._zip_files = [] self._tar_files = [] self._added_files = set() if zip_path: self._zip_files.append(zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED)) if tgz_path: self._tar_files.append(tarfile.open(tgz_path, "w:gz")) if txz_path: self._tar_files.append(tarfile.open(txz_path, "w:xz")) @property def added_files(self) -> set[str]: return self._added_files def add_file_data(self, arcpath: str, data: bytes, mode: int, time: datetime.datetime): for zf in self._zip_files: file_data_time = (time.year, time.month, time.day, time.hour, time.minute, time.second) zip_info = zipfile.ZipInfo(filename=arcpath, date_time=file_data_time) zip_info.external_attr = mode << 16 zip_info.compress_type = zipfile.ZIP_DEFLATED zf.writestr(zip_info, data=data) for tf in self._tar_files: tar_info = tarfile.TarInfo(arcpath) tar_info.type = tarfile.REGTYPE tar_info.mode = mode tar_info.size = len(data) tar_info.mtime = int(time.timestamp()) tf.addfile(tar_info, fileobj=io.BytesIO(data)) self._added_files.add(arcpath) def add_symlink(self, arcpath: str, target: str, time: datetime.datetime, files_for_zip): logger.debug("Adding symlink (target=%r) -> %s", target, arcpath) for zf in self._zip_files: file_data_time = (time.year, time.month, time.day, time.hour, time.minute, time.second) for f in files_for_zip: zip_info = zipfile.ZipInfo(filename=f["arcpath"], date_time=file_data_time) zip_info.external_attr = f["mode"] << 16 zip_info.compress_type = zipfile.ZIP_DEFLATED zf.writestr(zip_info, data=f["data"]) for tf in self._tar_files: tar_info = tarfile.TarInfo(arcpath) tar_info.type = tarfile.SYMTYPE tar_info.mode = 0o777 tar_info.mtime = int(time.timestamp()) tar_info.linkname = target tf.addfile(tar_info) self._added_files.update(f["arcpath"] for f in files_for_zip) def add_git_hash(self, arcdir: str, commit: str, time: datetime.datetime): arcpath = arc_join(arcdir, GIT_HASH_FILENAME) data = f"{commit}\n".encode() self.add_file_data(arcpath=arcpath, data=data, mode=0o100644, time=time) def add_file_path(self, arcpath: str, path: Path): assert path.is_file(), f"{path} should be a file" logger.debug("Adding %s -> %s", path, arcpath) for zf in self._zip_files: zf.write(path, arcname=arcpath) for tf in self._tar_files: tf.add(path, arcname=arcpath) def add_file_directory(self, arcdirpath: str, dirpath: Path): assert dirpath.is_dir() if arcdirpath and arcdirpath[-1:] != "/": arcdirpath += "/" for f in dirpath.iterdir(): if f.is_file(): arcpath = f"{arcdirpath}{f.name}" logger.debug("Adding %s to %s", f, arcpath) self.add_file_path(arcpath=arcpath, path=f) def close(self): # Archiver is intentionally made invalid after this function del self._zip_files self._zip_files = None del self._tar_files self._tar_files = None def __enter__(self): return self def __exit__(self, type, value, traceback): self.close() class NodeInArchive: def __init__(self, arcpath: str, path: typing.Optional[Path]=None, data: typing.Optional[bytes]=None, mode: typing.Optional[int]=None, symtarget: typing.Optional[str]=None, time: typing.Optional[datetime.datetime]=None, directory: bool=False): self.arcpath = arcpath self.path = path self.data = data self.mode = mode self.symtarget = symtarget self.time = time self.directory = directory @classmethod def from_fs(cls, arcpath: str, path: Path, mode: int=0o100644, time: typing.Optional[datetime.datetime]=None) -> "NodeInArchive": if time is None: time = datetime.datetime.fromtimestamp(os.stat(path).st_mtime) return cls(arcpath=arcpath, path=path, mode=mode) @classmethod def from_data(cls, arcpath: str, data: bytes, time: datetime.datetime) -> "NodeInArchive": return cls(arcpath=arcpath, data=data, time=time, mode=0o100644) @classmethod def from_text(cls, arcpath: str, text: str, time: datetime.datetime) -> "NodeInArchive": return cls.from_data(arcpath=arcpath, data=text.encode(), time=time) @classmethod def from_symlink(cls, arcpath: str, symtarget: str) -> "NodeInArchive": return cls(arcpath=arcpath, symtarget=symtarget) @classmethod def from_directory(cls, arcpath: str) -> "NodeInArchive": return cls(arcpath=arcpath, directory=True) def __repr__(self) -> str: return f"<{type(self).__name__}:arcpath={self.arcpath},path='{str(self.path)}',len(data)={len(self.data) if self.data else 'n/a'},directory={self.directory},symtarget={self.symtarget}>" def configure_file(path: Path, context: dict[str, str]) -> bytes: text = path.read_text() return configure_text(text, context=context).encode() def configure_text(text: str, context: dict[str, str]) -> str: original_text = text for txt, repl in context.items(): text = text.replace(f"@<@{txt}@>@", repl) success = all(thing not in text for thing in ("@<@", "@>@")) if not success: raise ValueError(f"Failed to configure {repr(original_text)}") return text def configure_text_list(text_list: list[str], context: dict[str, str]) -> list[str]: return [configure_text(text=e, context=context) for e in text_list] class ArchiveFileTree: def __init__(self): self._tree: dict[str, NodeInArchive] = {} def add_file(self, file: NodeInArchive): self._tree[file.arcpath] = file def __iter__(self) -> typing.Iterable[NodeInArchive]: yield from self._tree.values() def __contains__(self, value: str) -> bool: return value in self._tree def get_latest_mod_time(self) -> datetime.datetime: return max(item.time for item in self._tree.values() if item.time) def add_to_archiver(self, archive_base: str, archiver: Archiver): remaining_symlinks = set() added_files = dict() def calculate_symlink_target(s: NodeInArchive) -> str: dest_dir = os.path.dirname(s.arcpath) if dest_dir: dest_dir += "/" target = dest_dir + s.symtarget while True: new_target, n = re.subn(r"([^/]+/+[.]{2}/)", "", target) target = new_target if not n: break return target # Add files in first pass for arcpath, node in self._tree.items(): assert node is not None, f"{arcpath} -> node" if node.data is not None: archiver.add_file_data(arcpath=arc_join(archive_base, arcpath), data=node.data, time=node.time, mode=node.mode) assert node.arcpath is not None, f"{node=}" added_files[node.arcpath] = node elif node.path is not None: archiver.add_file_path(arcpath=arc_join(archive_base, arcpath), path=node.path) assert node.arcpath is not None, f"{node=}" added_files[node.arcpath] = node elif node.symtarget is not None: remaining_symlinks.add(node) elif node.directory: pass else: raise ValueError(f"Invalid Archive Node: {repr(node)}") assert None not in added_files # Resolve symlinks in second pass: zipfile does not support symlinks, so add files to zip archive while True: if not remaining_symlinks: break symlinks_this_time = set() extra_added_files = {} for symlink in remaining_symlinks: symlink_files_for_zip = {} symlink_target_path = calculate_symlink_target(symlink) if symlink_target_path in added_files: symlink_files_for_zip[symlink.arcpath] = added_files[symlink_target_path] else: symlink_target_path_slash = symlink_target_path + "/" for added_file in added_files: if added_file.startswith(symlink_target_path_slash): path_in_symlink = symlink.arcpath + "/" + added_file.removeprefix(symlink_target_path_slash) symlink_files_for_zip[path_in_symlink] = added_files[added_file] if symlink_files_for_zip: symlinks_this_time.add(symlink) extra_added_files.update(symlink_files_for_zip) files_for_zip = [{"arcpath": f"{archive_base}/{sym_path}", "data": sym_info.data, "mode": sym_info.mode} for sym_path, sym_info in symlink_files_for_zip.items()] archiver.add_symlink(arcpath=f"{archive_base}/{symlink.arcpath}", target=symlink.symtarget, time=symlink.time, files_for_zip=files_for_zip) # if not symlinks_this_time: # logger.info("files added: %r", set(path for path in added_files.keys())) assert symlinks_this_time, f"No targets found for symlinks: {remaining_symlinks}" remaining_symlinks.difference_update(symlinks_this_time) added_files.update(extra_added_files) def add_directory_tree(self, arc_dir: str, path: Path, time: datetime.datetime): assert path.is_dir() for files_dir, _, filenames in os.walk(path): files_dir_path = Path(files_dir) rel_files_path = files_dir_path.relative_to(path) for filename in filenames: self.add_file(NodeInArchive.from_fs(arcpath=arc_join(arc_dir, str(rel_files_path), filename), path=files_dir_path / filename, time=time)) def _add_files_recursively(self, arc_dir: str, paths: list[Path], time: datetime.datetime): logger.debug(f"_add_files_recursively({arc_dir=} {paths=})") for path in paths: arcpath = arc_join(arc_dir, path.name) if path.is_file(): logger.debug("Adding %s as %s", path, arcpath) self.add_file(NodeInArchive.from_fs(arcpath=arcpath, path=path, time=time)) elif path.is_dir(): self._add_files_recursively(arc_dir=arc_join(arc_dir, path.name), paths=list(path.iterdir()), time=time) else: raise ValueError(f"Unsupported file type to add recursively: {path}") def add_file_mapping(self, arc_dir: str, file_mapping: dict[str, list[str]], file_mapping_root: Path, context: dict[str, str], time: datetime.datetime): for meta_rel_destdir, meta_file_globs in file_mapping.items(): rel_destdir = configure_text(meta_rel_destdir, context=context) assert "@" not in rel_destdir, f"archive destination should not contain an @ after configuration ({repr(meta_rel_destdir)}->{repr(rel_destdir)})" for meta_file_glob in meta_file_globs: file_glob = configure_text(meta_file_glob, context=context) assert "@" not in rel_destdir, f"archive glob should not contain an @ after configuration ({repr(meta_file_glob)}->{repr(file_glob)})" if ":" in file_glob: original_path, new_filename = file_glob.rsplit(":", 1) assert ":" not in original_path, f"Too many ':' in {repr(file_glob)}" assert "/" not in new_filename, f"New filename cannot contain a '/' in {repr(file_glob)}" path = file_mapping_root / original_path arcpath = arc_join(arc_dir, rel_destdir, new_filename) if path.suffix == ".in": data = configure_file(path, context=context) logger.debug("Adding processed %s -> %s", path, arcpath) self.add_file(NodeInArchive.from_data(arcpath=arcpath, data=data, time=time)) else: logger.debug("Adding %s -> %s", path, arcpath) self.add_file(NodeInArchive.from_fs(arcpath=arcpath, path=path, time=time)) else: relative_file_paths = glob.glob(file_glob, root_dir=file_mapping_root) assert relative_file_paths, f"Glob '{file_glob}' does not match any file" self._add_files_recursively(arc_dir=arc_join(arc_dir, rel_destdir), paths=[file_mapping_root / p for p in relative_file_paths], time=time) class SourceCollector: # TreeItem = collections.namedtuple("TreeItem", ("path", "mode", "data", "symtarget", "directory", "time")) def __init__(self, root: Path, commit: str, filter: typing.Optional[Callable[[str], bool]], executer: Executer): self.root = root self.commit = commit self.filter = filter self.executer = executer def get_archive_file_tree(self) -> ArchiveFileTree: git_archive_args = ["git", "archive", "--format=tar.gz", self.commit, "-o", "/dev/stdout"] logger.info("Executing args=%r", git_archive_args) contents_tgz = subprocess.check_output(git_archive_args, cwd=self.root, text=False) tar_archive = tarfile.open(fileobj=io.BytesIO(contents_tgz), mode="r:gz") filenames = tuple(m.name for m in tar_archive if (m.isfile() or m.issym())) file_times = self._get_file_times(paths=filenames) git_contents = ArchiveFileTree() for ti in tar_archive: if self.filter and not self.filter(ti.name): continue data = None symtarget = None directory = False file_time = None if ti.isfile(): contents_file = tar_archive.extractfile(ti.name) data = contents_file.read() file_time = file_times[ti.name] elif ti.issym(): symtarget = ti.linkname file_time = file_times[ti.name] elif ti.isdir(): directory = True else: raise ValueError(f"{ti.name}: unknown type") node = NodeInArchive(arcpath=ti.name, data=data, mode=ti.mode, symtarget=symtarget, time=file_time, directory=directory) git_contents.add_file(node) return git_contents def _get_file_times(self, paths: tuple[str, ...]) -> dict[str, datetime.datetime]: dry_out = textwrap.dedent("""\ time=2024-03-14T15:40:25-07:00 M\tCMakeLists.txt """) git_log_out = self.executer.check_output(["git", "log", "--name-status", '--pretty=time=%cI', self.commit], dry_out=dry_out, cwd=self.root).splitlines(keepends=False) current_time = None set_paths = set(paths) path_times: dict[str, datetime.datetime] = {} for line in git_log_out: if not line: continue if line.startswith("time="): current_time = safe_isotime_to_datetime(line.removeprefix("time=")) continue mod_type, file_paths = line.split(maxsplit=1) assert current_time is not None for file_path in file_paths.split("\t"): if file_path in set_paths and file_path not in path_times: path_times[file_path] = current_time # FIXME: find out why some files are not shown in "git log" # assert set(path_times.keys()) == set_paths if set(path_times.keys()) != set_paths: found_times = set(path_times.keys()) paths_without_times = set_paths.difference(found_times) logger.warning("No times found for these paths: %s", paths_without_times) max_time = max(time for time in path_times.values()) for path in paths_without_times: path_times[path] = max_time return path_times class Releaser: def __init__(self, release_info: dict, commit: str, revision: str, root: Path, dist_path: Path, section_printer: SectionPrinter, executer: Executer, cmake_generator: str, deps_path: Path, overwrite: bool, github: bool, fast: bool): self.release_info = release_info self.project = release_info["name"] self.version = self.extract_sdl_version(root=root, release_info=release_info) self.root = root self.commit = commit self.revision = revision self.dist_path = dist_path self.section_printer = section_printer self.executer = executer self.cmake_generator = cmake_generator self.cpu_count = multiprocessing.cpu_count() self.deps_path = deps_path self.overwrite = overwrite self.github = github self.fast = fast self.arc_time = datetime.datetime.now() self.artifacts: dict[str, Path] = {} def get_context(self, extra_context: typing.Optional[dict[str, str]]=None) -> dict[str, str]: ctx = { "PROJECT_NAME": self.project, "PROJECT_VERSION": self.version, "PROJECT_COMMIT": self.commit, "PROJECT_REVISION": self.revision, "PROJECT_ROOT": str(self.root), } if extra_context: ctx.update(extra_context) return ctx @property def dry(self) -> bool: return self.executer.dry def prepare(self): logger.debug("Creating dist folder") self.dist_path.mkdir(parents=True, exist_ok=True) @classmethod def _path_filter(cls, path: str) -> bool: if ".gitmodules" in path: return True if path.startswith(".git"): return False return True @classmethod def _external_repo_path_filter(cls, path: str) -> bool: if not cls._path_filter(path): return False if path.startswith("test/") or path.startswith("tests/"): return False return True def create_source_archives(self) -> None: source_collector = SourceCollector(root=self.root, commit=self.commit, executer=self.executer, filter=self._path_filter) print(f"Collecting sources of {self.project}...") archive_tree: ArchiveFileTree = source_collector.get_archive_file_tree() latest_mod_time = archive_tree.get_latest_mod_time() archive_tree.add_file(NodeInArchive.from_text(arcpath=REVISION_TXT, text=f"{self.revision}\n", time=latest_mod_time)) archive_tree.add_file(NodeInArchive.from_text(arcpath=f"{GIT_HASH_FILENAME}", text=f"{self.commit}\n", time=latest_mod_time)) archive_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["source"].get("files", {}), file_mapping_root=self.root, context=self.get_context(), time=latest_mod_time) if "Makefile.am" in archive_tree: patched_time = latest_mod_time + datetime.timedelta(minutes=1) print(f"Makefile.am detected -> touching aclocal.m4, */Makefile.in, configure") for node_data in archive_tree: arc_name = os.path.basename(node_data.arcpath) arc_name_we, arc_name_ext = os.path.splitext(arc_name) if arc_name in ("aclocal.m4", "configure", "Makefile.in"): print(f"Bumping time of {node_data.arcpath}") node_data.time = patched_time archive_base = f"{self.project}-{self.version}" zip_path = self.dist_path / f"{archive_base}.zip" tgz_path = self.dist_path / f"{archive_base}.tar.gz" txz_path = self.dist_path / f"{archive_base}.tar.xz" logger.info("Creating zip/tgz/txz source archives ...") if self.dry: zip_path.touch() tgz_path.touch() txz_path.touch() else: with Archiver(zip_path=zip_path, tgz_path=tgz_path, txz_path=txz_path) as archiver: print(f"Adding source files of {self.project}...") archive_tree.add_to_archiver(archive_base=archive_base, archiver=archiver) for extra_repo in self.release_info["source"].get("extra-repos", []): extra_repo_root = self.root / extra_repo assert (extra_repo_root / ".git").exists(), f"{extra_repo_root} must be a git repo" extra_repo_commit = self.executer.check_output(["git", "rev-parse", "HEAD"], dry_out=f"gitsha-extra-repo-{extra_repo}", cwd=extra_repo_root).strip() extra_repo_source_collector = SourceCollector(root=extra_repo_root, commit=extra_repo_commit, executer=self.executer, filter=self._external_repo_path_filter) print(f"Collecting sources of {extra_repo} ...") extra_repo_archive_tree = extra_repo_source_collector.get_archive_file_tree() print(f"Adding source files of {extra_repo} ...") extra_repo_archive_tree.add_to_archiver(archive_base=f"{archive_base}/{extra_repo}", archiver=archiver) for file in self.release_info["source"]["checks"]: assert f"{archive_base}/{file}" in archiver.added_files, f"'{archive_base}/{file}' must exist" logger.info("... done") self.artifacts["src-zip"] = zip_path self.artifacts["src-tar-gz"] = tgz_path self.artifacts["src-tar-xz"] = txz_path if not self.dry: with tgz_path.open("r+b") as f: # Zero the embedded timestamp in the gzip'ed tarball f.seek(4, 0) f.write(b"\x00\x00\x00\x00") def create_dmg(self, configuration: str="Release") -> None: dmg_in = self.root / self.release_info["dmg"]["path"] xcode_project = self.root / self.release_info["dmg"]["project"] assert xcode_project.is_dir(), f"{xcode_project} must be a directory" assert (xcode_project / "project.pbxproj").is_file, f"{xcode_project} must contain project.pbxproj" if not self.fast: dmg_in.unlink(missing_ok=True) build_xcconfig = self.release_info["dmg"].get("build-xcconfig") if build_xcconfig: shutil.copy(self.root / build_xcconfig, xcode_project.parent / "build.xcconfig") xcode_scheme = self.release_info["dmg"].get("scheme") xcode_target = self.release_info["dmg"].get("target") assert xcode_scheme or xcode_target, "dmg needs scheme or target" assert not (xcode_scheme and xcode_target), "dmg cannot have both scheme and target set" if xcode_scheme: scheme_or_target = "-scheme" target_like = xcode_scheme else: scheme_or_target = "-target" target_like = xcode_target self.executer.run(["xcodebuild", "ONLY_ACTIVE_ARCH=NO", "-project", xcode_project, scheme_or_target, target_like, "-configuration", configuration]) if self.dry: dmg_in.parent.mkdir(parents=True, exist_ok=True) dmg_in.touch() assert dmg_in.is_file(), f"{self.project}.dmg was not created by xcodebuild" dmg_out = self.dist_path / f"{self.project}-{self.version}.dmg" shutil.copy(dmg_in, dmg_out) self.artifacts["dmg"] = dmg_out @property def git_hash_data(self) -> bytes: return f"{self.commit}\n".encode() def create_mingw_archives(self) -> None: build_type = "Release" build_parent_dir = self.root / "build-mingw" ARCH_TO_GNU_ARCH = { # "arm64": "aarch64", "x86": "i686", "x64": "x86_64", } ARCH_TO_TRIPLET = { # "arm64": "aarch64-w64-mingw32", "x86": "i686-w64-mingw32", "x64": "x86_64-w64-mingw32", } new_env = dict(os.environ) cmake_prefix_paths = [] mingw_deps_path = self.deps_path / "mingw-deps" if "dependencies" in self.release_info["mingw"]: shutil.rmtree(mingw_deps_path, ignore_errors=True) mingw_deps_path.mkdir() for triplet in ARCH_TO_TRIPLET.values(): (mingw_deps_path / triplet).mkdir() def extract_filter(member: tarfile.TarInfo, path: str, /): if member.name.startswith("SDL"): member.name = "/".join(Path(member.name).parts[1:]) return member for dep in self.release_info.get("dependencies", {}): extract_path = mingw_deps_path / f"extract-{dep}" extract_path.mkdir() with chdir(extract_path): tar_path = self.deps_path / glob.glob(self.release_info["mingw"]["dependencies"][dep]["artifact"], root_dir=self.deps_path)[0] logger.info("Extracting %s to %s", tar_path, mingw_deps_path) assert tar_path.suffix in (".gz", ".xz") with tarfile.open(tar_path, mode=f"r:{tar_path.suffix.strip('.')}") as tarf: tarf.extractall(filter=extract_filter) for arch, triplet in ARCH_TO_TRIPLET.items(): install_cmd = self.release_info["mingw"]["dependencies"][dep]["install-command"] extra_configure_data = { "ARCH": ARCH_TO_GNU_ARCH[arch], "TRIPLET": triplet, "PREFIX": str(mingw_deps_path / triplet), } install_cmd = configure_text(install_cmd, context=self.get_context(extra_configure_data)) self.executer.run(shlex.split(install_cmd), cwd=str(extract_path)) dep_binpath = mingw_deps_path / triplet / "bin" assert dep_binpath.is_dir(), f"{dep_binpath} for PATH should exist" dep_pkgconfig = mingw_deps_path / triplet / "lib/pkgconfig" assert dep_pkgconfig.is_dir(), f"{dep_pkgconfig} for PKG_CONFIG_PATH should exist" new_env["PATH"] = os.pathsep.join([str(dep_binpath), new_env["PATH"]]) new_env["PKG_CONFIG_PATH"] = str(dep_pkgconfig) cmake_prefix_paths.append(mingw_deps_path) new_env["CFLAGS"] = f"-O2 -ffile-prefix-map={self.root}=/src/{self.project}" new_env["CXXFLAGS"] = f"-O2 -ffile-prefix-map={self.root}=/src/{self.project}" assert any(system in self.release_info["mingw"] for system in ("autotools", "cmake")) assert not all(system in self.release_info["mingw"] for system in ("autotools", "cmake")) mingw_archs = set() arc_root = f"{self.project}-{self.version}" archive_file_tree = ArchiveFileTree() if "autotools" in self.release_info["mingw"]: for arch in self.release_info["mingw"]["autotools"]["archs"]: triplet = ARCH_TO_TRIPLET[arch] new_env["CC"] = f"{triplet}-gcc" new_env["CXX"] = f"{triplet}-g++" new_env["RC"] = f"{triplet}-windres" assert arch not in mingw_archs mingw_archs.add(arch) build_path = build_parent_dir / f"build-{triplet}" install_path = build_parent_dir / f"install-{triplet}" shutil.rmtree(install_path, ignore_errors=True) build_path.mkdir(parents=True, exist_ok=True) context = self.get_context({ "ARCH": arch, "DEP_PREFIX": str(mingw_deps_path / triplet), }) extra_args = configure_text_list(text_list=self.release_info["mingw"]["autotools"]["args"], context=context) with self.section_printer.group(f"Configuring MinGW {triplet} (autotools)"): assert "@" not in " ".join(extra_args), f"@ should not be present in extra arguments ({extra_args})" self.executer.run([ self.root / "configure", f"--prefix={install_path}", f"--includedir=${{prefix}}/include", f"--libdir=${{prefix}}/lib", f"--bindir=${{prefix}}/bin", f"--host={triplet}", f"--build=x86_64-none-linux-gnu", "CFLAGS=-O2", "CXXFLAGS=-O2", "LDFLAGS=-Wl,-s", ] + extra_args, cwd=build_path, env=new_env) with self.section_printer.group(f"Build MinGW {triplet} (autotools)"): self.executer.run(["make", f"-j{self.cpu_count}"], cwd=build_path, env=new_env) with self.section_printer.group(f"Install MinGW {triplet} (autotools)"): self.executer.run(["make", "install"], cwd=build_path, env=new_env) archive_file_tree.add_directory_tree(arc_dir=arc_join(arc_root, triplet), path=install_path, time=self.arc_time) print("Recording arch-dependent extra files for MinGW development archive ...") extra_context = { "TRIPLET": ARCH_TO_TRIPLET[arch], } archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["mingw"]["autotools"].get("files", {}), file_mapping_root=self.root, context=self.get_context(extra_context=extra_context), time=self.arc_time) if "cmake" in self.release_info["mingw"]: assert self.release_info["mingw"]["cmake"]["shared-static"] in ("args", "both") for arch in self.release_info["mingw"]["cmake"]["archs"]: triplet = ARCH_TO_TRIPLET[arch] new_env["CC"] = f"{triplet}-gcc" new_env["CXX"] = f"{triplet}-g++" new_env["RC"] = f"{triplet}-windres" assert arch not in mingw_archs mingw_archs.add(arch) context = self.get_context({ "ARCH": arch, "DEP_PREFIX": str(mingw_deps_path / triplet), }) extra_args = configure_text_list(text_list=self.release_info["mingw"]["cmake"]["args"], context=context) build_path = build_parent_dir / f"build-{triplet}" install_path = build_parent_dir / f"install-{triplet}" shutil.rmtree(install_path, ignore_errors=True) build_path.mkdir(parents=True, exist_ok=True) if self.release_info["mingw"]["cmake"]["shared-static"] == "args": args_for_shared_static = ([], ) elif self.release_info["mingw"]["cmake"]["shared-static"] == "both": args_for_shared_static = (["-DBUILD_SHARED_LIBS=ON"], ["-DBUILD_SHARED_LIBS=OFF"]) for arg_for_shared_static in args_for_shared_static: with self.section_printer.group(f"Configuring MinGW {triplet} (CMake)"): assert "@" not in " ".join(extra_args), f"@ should not be present in extra arguments ({extra_args})" self.executer.run([ f"cmake", f"-S", str(self.root), "-B", str(build_path), f"-DCMAKE_BUILD_TYPE={build_type}", f'''-DCMAKE_C_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', f'''-DCMAKE_CXX_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', f"-DCMAKE_PREFIX_PATH={mingw_deps_path / triplet}", f"-DCMAKE_INSTALL_PREFIX={install_path}", f"-DCMAKE_INSTALL_INCLUDEDIR=include", f"-DCMAKE_INSTALL_LIBDIR=lib", f"-DCMAKE_INSTALL_BINDIR=bin", f"-DCMAKE_INSTALL_DATAROOTDIR=share", f"-DCMAKE_TOOLCHAIN_FILE={self.root}/build-scripts/cmake-toolchain-mingw64-{ARCH_TO_GNU_ARCH[arch]}.cmake", f"-G{self.cmake_generator}", ] + extra_args + ([] if self.fast else ["--fresh"]) + arg_for_shared_static, cwd=build_path, env=new_env) with self.section_printer.group(f"Build MinGW {triplet} (CMake)"): self.executer.run(["cmake", "--build", str(build_path), "--verbose", "--config", build_type], cwd=build_path, env=new_env) with self.section_printer.group(f"Install MinGW {triplet} (CMake)"): self.executer.run(["cmake", "--install", str(build_path)], cwd=build_path, env=new_env) archive_file_tree.add_directory_tree(arc_dir=arc_join(arc_root, triplet), path=install_path, time=self.arc_time) print("Recording arch-dependent extra files for MinGW development archive ...") extra_context = { "TRIPLET": ARCH_TO_TRIPLET[arch], } archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["mingw"]["cmake"].get("files", {}), file_mapping_root=self.root, context=self.get_context(extra_context=extra_context), time=self.arc_time) print("... done") print("Recording extra files for MinGW development archive ...") archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["mingw"].get("files", {}), file_mapping_root=self.root, context=self.get_context(), time=self.arc_time) print("... done") print("Creating zip/tgz/txz development archives ...") zip_path = self.dist_path / f"{self.project}-devel-{self.version}-mingw.zip" tgz_path = self.dist_path / f"{self.project}-devel-{self.version}-mingw.tar.gz" txz_path = self.dist_path / f"{self.project}-devel-{self.version}-mingw.tar.xz" with Archiver(zip_path=zip_path, tgz_path=tgz_path, txz_path=txz_path) as archiver: archive_file_tree.add_to_archiver(archive_base="", archiver=archiver) archiver.add_git_hash(arcdir=arc_root, commit=self.commit, time=self.arc_time) print("... done") self.artifacts["mingw-devel-zip"] = zip_path self.artifacts["mingw-devel-tar-gz"] = tgz_path self.artifacts["mingw-devel-tar-xz"] = txz_path def _detect_android_api(self, android_home: str) -> typing.Optional[int]: platform_dirs = list(Path(p) for p in glob.glob(f"{android_home}/platforms/android-*")) re_platform = re.compile("android-([0-9]+)") platform_versions = [] for platform_dir in platform_dirs: logger.debug("Found Android Platform SDK: %s", platform_dir) if m:= re_platform.match(platform_dir.name): platform_versions.append(int(m.group(1))) platform_versions.sort() logger.info("Available platform versions: %s", platform_versions) platform_versions = list(filter(lambda v: v >= self._android_api_minimum, platform_versions)) logger.info("Valid platform versions (>=%d): %s", self._android_api_minimum, platform_versions) if not platform_versions: return None android_api = platform_versions[0] logger.info("Selected API version %d", android_api) return android_api def _get_prefab_json_text(self) -> str: return textwrap.dedent(f"""\ {{ "schema_version": 2, "name": "{self.project}", "version": "{self.version}", "dependencies": [] }} """) def _get_prefab_module_json_text(self, library_name: typing.Optional[str], export_libraries: list[str]) -> str: for lib in export_libraries: assert isinstance(lib, str), f"{lib} must be a string" module_json_dict = { "export_libraries": export_libraries, } if library_name: module_json_dict["library_name"] = f"lib{library_name}" return json.dumps(module_json_dict, indent=4) @property def _android_api_minimum(self): return self.release_info["android"]["api-minimum"] @property def _android_api_target(self): return self.release_info["android"]["api-target"] @property def _android_ndk_minimum(self): return self.release_info["android"]["ndk-minimum"] def _get_prefab_abi_json_text(self, abi: str, cpp: bool, shared: bool) -> str: abi_json_dict = { "abi": abi, "api": self._android_api_minimum, "ndk": self._android_ndk_minimum, "stl": "c++_shared" if cpp else "none", "static": not shared, } return json.dumps(abi_json_dict, indent=4) def _get_android_manifest_text(self) -> str: return textwrap.dedent(f"""\ """) def create_android_archives(self, android_api: int, android_home: Path, android_ndk_home: Path) -> None: cmake_toolchain_file = Path(android_ndk_home) / "build/cmake/android.toolchain.cmake" if not cmake_toolchain_file.exists(): logger.error("CMake toolchain file does not exist (%s)", cmake_toolchain_file) raise SystemExit(1) aar_path = self.dist_path / f"{self.project}-{self.version}.aar" android_abis = self.release_info["android"]["abis"] java_jars_added = False module_data_added = False android_deps_path = self.deps_path / "android-deps" shutil.rmtree(android_deps_path, ignore_errors=True) for dep, depinfo in self.release_info["android"].get("dependencies", {}).items(): android_aar = self.deps_path / glob.glob(depinfo["artifact"], root_dir=self.deps_path)[0] with self.section_printer.group(f"Extracting Android dependency {dep} ({android_aar.name})"): self.executer.run([sys.executable, str(android_aar), "-o", str(android_deps_path)]) for module_name, module_info in self.release_info["android"]["modules"].items(): assert "type" in module_info and module_info["type"] in ("interface", "library"), f"module {module_name} must have a valid type" archive_file_tree = ArchiveFileTree() for android_abi in android_abis: with self.section_printer.group(f"Building for Android {android_api} {android_abi}"): build_dir = self.root / "build-android" / f"{android_abi}-build" install_dir = self.root / "install-android" / f"{android_abi}-install" shutil.rmtree(install_dir, ignore_errors=True) assert not install_dir.is_dir(), f"{install_dir} should not exist prior to build" build_type = "Release" cmake_args = [ "cmake", "-S", str(self.root), "-B", str(build_dir), f'''-DCMAKE_C_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', f'''-DCMAKE_CXX_FLAGS="-ffile-prefix-map={self.root}=/src/{self.project}"''', f"-DCMAKE_TOOLCHAIN_FILE={cmake_toolchain_file}", f"-DCMAKE_PREFIX_PATH={str(android_deps_path)}", f"-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH", f"-DANDROID_HOME={android_home}", f"-DANDROID_PLATFORM={android_api}", f"-DANDROID_ABI={android_abi}", "-DCMAKE_POSITION_INDEPENDENT_CODE=ON", f"-DCMAKE_INSTALL_PREFIX={install_dir}", "-DCMAKE_INSTALL_INCLUDEDIR=include ", "-DCMAKE_INSTALL_LIBDIR=lib", "-DCMAKE_INSTALL_DATAROOTDIR=share", f"-DCMAKE_BUILD_TYPE={build_type}", f"-G{self.cmake_generator}", ] + self.release_info["android"]["cmake"]["args"] + ([] if self.fast else ["--fresh"]) build_args = [ "cmake", "--build", str(build_dir), "--verbose", "--config", build_type, ] install_args = [ "cmake", "--install", str(build_dir), "--config", build_type, ] self.executer.run(cmake_args) self.executer.run(build_args) self.executer.run(install_args) for module_name, module_info in self.release_info["android"]["modules"].items(): arcdir_prefab_module = f"prefab/modules/{module_name}" if module_info["type"] == "library": library = install_dir / module_info["library"] assert library.suffix in (".so", ".a") assert library.is_file(), f"CMake should have built library '{library}' for module {module_name}" arcdir_prefab_libs = f"{arcdir_prefab_module}/libs/android.{android_abi}" archive_file_tree.add_file(NodeInArchive.from_fs(arcpath=f"{arcdir_prefab_libs}/{library.name}", path=library, time=self.arc_time)) archive_file_tree.add_file(NodeInArchive.from_text(arcpath=f"{arcdir_prefab_libs}/abi.json", text=self._get_prefab_abi_json_text(abi=android_abi, cpp=False, shared=library.suffix == ".so"), time=self.arc_time)) if not module_data_added: library_name = None if module_info["type"] == "library": library_name = Path(module_info["library"]).stem.removeprefix("lib") export_libraries = module_info.get("export-libraries", []) archive_file_tree.add_file(NodeInArchive.from_text(arcpath=arc_join(arcdir_prefab_module, "module.json"), text=self._get_prefab_module_json_text(library_name=library_name, export_libraries=export_libraries), time=self.arc_time)) arcdir_prefab_include = f"prefab/modules/{module_name}/include" if "includes" in module_info: archive_file_tree.add_file_mapping(arc_dir=arcdir_prefab_include, file_mapping=module_info["includes"], file_mapping_root=install_dir, context=self.get_context(), time=self.arc_time) else: archive_file_tree.add_file(NodeInArchive.from_text(arcpath=arc_join(arcdir_prefab_include, ".keep"), text="\n", time=self.arc_time)) module_data_added = True if not java_jars_added: java_jars_added = True if "jars" in self.release_info["android"]: classes_jar_path = install_dir / configure_text(text=self.release_info["android"]["jars"]["classes"], context=self.get_context()) sources_jar_path = install_dir / configure_text(text=self.release_info["android"]["jars"]["sources"], context=self.get_context()) doc_jar_path = install_dir / configure_text(text=self.release_info["android"]["jars"]["doc"], context=self.get_context()) assert classes_jar_path.is_file(), f"CMake should have compiled the java sources and archived them into a JAR ({classes_jar_path})" assert sources_jar_path.is_file(), f"CMake should have archived the java sources into a JAR ({sources_jar_path})" assert doc_jar_path.is_file(), f"CMake should have archived javadoc into a JAR ({doc_jar_path})" archive_file_tree.add_file(NodeInArchive.from_fs(arcpath="classes.jar", path=classes_jar_path, time=self.arc_time)) archive_file_tree.add_file(NodeInArchive.from_fs(arcpath="classes-sources.jar", path=sources_jar_path, time=self.arc_time)) archive_file_tree.add_file(NodeInArchive.from_fs(arcpath="classes-doc.jar", path=doc_jar_path, time=self.arc_time)) assert ("jars" in self.release_info["android"] and java_jars_added) or "jars" not in self.release_info["android"], "Must have archived java JAR archives" archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["android"].get("files", {}), file_mapping_root=self.root, context=self.get_context(), time=self.arc_time) archive_file_tree.add_file(NodeInArchive.from_text(arcpath="prefab/prefab.json", text=self._get_prefab_json_text(), time=self.arc_time)) archive_file_tree.add_file(NodeInArchive.from_text(arcpath="AndroidManifest.xml", text=self._get_android_manifest_text(), time=self.arc_time)) with Archiver(zip_path=aar_path) as archiver: archive_file_tree.add_to_archiver(archive_base="", archiver=archiver) archiver.add_git_hash(arcdir="", commit=self.commit, time=self.arc_time) self.artifacts[f"android-aar"] = aar_path def download_dependencies(self): shutil.rmtree(self.deps_path, ignore_errors=True) self.deps_path.mkdir(parents=True) if self.github: with open(os.environ["GITHUB_OUTPUT"], "a") as f: f.write(f"dep-path={self.deps_path.absolute()}\n") for dep, depinfo in self.release_info.get("dependencies", {}).items(): startswith = depinfo["startswith"] dep_repo = depinfo["repo"] # FIXME: dropped "--exclude-pre-releases" dep_string_data = self.executer.check_output(["gh", "-R", dep_repo, "release", "list", "--exclude-drafts", "--json", "name,createdAt,tagName", "--jq", f'[.[]|select(.name|startswith("{startswith}"))]|max_by(.createdAt)']).strip() dep_data = json.loads(dep_string_data) dep_tag = dep_data["tagName"] dep_version = dep_data["name"] logger.info("Download dependency %s version %s (tag=%s) ", dep, dep_version, dep_tag) self.executer.run(["gh", "-R", dep_repo, "release", "download", dep_tag], cwd=self.deps_path) if self.github: with open(os.environ["GITHUB_OUTPUT"], "a") as f: f.write(f"dep-{dep.lower()}-version={dep_version}\n") def verify_dependencies(self): for dep, depinfo in self.release_info.get("dependencies", {}).items(): if "mingw" in self.release_info: mingw_matches = glob.glob(self.release_info["mingw"]["dependencies"][dep]["artifact"], root_dir=self.deps_path) assert len(mingw_matches) == 1, f"Exactly one archive matches mingw {dep} dependency: {mingw_matches}" if "dmg" in self.release_info: dmg_matches = glob.glob(self.release_info["dmg"]["dependencies"][dep]["artifact"], root_dir=self.deps_path) assert len(dmg_matches) == 1, f"Exactly one archive matches dmg {dep} dependency: {dmg_matches}" if "msvc" in self.release_info: msvc_matches = glob.glob(self.release_info["msvc"]["dependencies"][dep]["artifact"], root_dir=self.deps_path) assert len(msvc_matches) == 1, f"Exactly one archive matches msvc {dep} dependency: {msvc_matches}" if "android" in self.release_info: android_matches = glob.glob(self.release_info["android"]["dependencies"][dep]["artifact"], root_dir=self.deps_path) assert len(android_matches) == 1, f"Exactly one archive matches msvc {dep} dependency: {msvc_matches}" @staticmethod def _arch_to_vs_platform(arch: str, configuration: str="Release") -> VsArchPlatformConfig: ARCH_TO_VS_PLATFORM = { "x86": VsArchPlatformConfig(arch="x86", platform="Win32", configuration=configuration), "x64": VsArchPlatformConfig(arch="x64", platform="x64", configuration=configuration), "arm64": VsArchPlatformConfig(arch="arm64", platform="ARM64", configuration=configuration), } return ARCH_TO_VS_PLATFORM[arch] def build_msvc(self): with self.section_printer.group("Find Visual Studio"): vs = VisualStudio(executer=self.executer) for arch in self.release_info["msvc"].get("msbuild", {}).get("archs", []): self._build_msvc_msbuild(arch_platform=self._arch_to_vs_platform(arch=arch), vs=vs) if "cmake" in self.release_info["msvc"]: deps_path = self.root / "msvc-deps" shutil.rmtree(deps_path, ignore_errors=True) dep_roots = [] for dep, depinfo in self.release_info["msvc"].get("dependencies", {}).items(): dep_extract_path = deps_path / f"extract-{dep}" msvc_zip = self.deps_path / glob.glob(depinfo["artifact"], root_dir=self.deps_path)[0] with zipfile.ZipFile(msvc_zip, "r") as zf: zf.extractall(dep_extract_path) contents_msvc_zip = glob.glob(str(dep_extract_path / "*")) assert len(contents_msvc_zip) == 1, f"There must be exactly one root item in the root directory of {dep}" dep_roots.append(contents_msvc_zip[0]) for arch in self.release_info["msvc"].get("cmake", {}).get("archs", []): self._build_msvc_cmake(arch_platform=self._arch_to_vs_platform(arch=arch), dep_roots=dep_roots) with self.section_printer.group("Create SDL VC development zip"): self._build_msvc_devel() def _build_msvc_msbuild(self, arch_platform: VsArchPlatformConfig, vs: VisualStudio): platform_context = self.get_context(arch_platform.extra_context()) for dep, depinfo in self.release_info["msvc"].get("dependencies", {}).items(): msvc_zip = self.deps_path / glob.glob(depinfo["artifact"], root_dir=self.deps_path)[0] src_globs = [configure_text(instr["src"], context=platform_context) for instr in depinfo["copy"]] with zipfile.ZipFile(msvc_zip, "r") as zf: for member in zf.namelist(): member_path = "/".join(Path(member).parts[1:]) for src_i, src_glob in enumerate(src_globs): if fnmatch.fnmatch(member_path, src_glob): dst = (self.root / configure_text(depinfo["copy"][src_i]["dst"], context=platform_context)).resolve() / Path(member_path).name zip_data = zf.read(member) if dst.exists(): identical = False if dst.is_file(): orig_bytes = dst.read_bytes() if orig_bytes == zip_data: identical = True if not identical: logger.warning("Extracting dependency %s, will cause %s to be overwritten", dep, dst) if not self.overwrite: raise RuntimeError("Run with --overwrite to allow overwriting") logger.debug("Extracting %s -> %s", member, dst) dst.parent.mkdir(exist_ok=True, parents=True) dst.write_bytes(zip_data) prebuilt_paths = set(self.root / full_prebuilt_path for prebuilt_path in self.release_info["msvc"]["msbuild"].get("prebuilt", []) for full_prebuilt_path in glob.glob(configure_text(prebuilt_path, context=platform_context), root_dir=self.root)) msbuild_paths = set(self.root / configure_text(f, context=platform_context) for file_mapping in (self.release_info["msvc"]["msbuild"]["files-lib"], self.release_info["msvc"]["msbuild"]["files-devel"]) for files_list in file_mapping.values() for f in files_list) assert prebuilt_paths.issubset(msbuild_paths), f"msvc.msbuild.prebuilt must be a subset of (msvc.msbuild.files-lib, msvc.msbuild.files-devel)" built_paths = msbuild_paths.difference(prebuilt_paths) logger.info("MSbuild builds these files, to be included in the package: %s", built_paths) if not self.fast: for b in built_paths: b.unlink(missing_ok=True) rel_projects: list[str] = self.release_info["msvc"]["msbuild"]["projects"] projects = list(self.root / p for p in rel_projects) directory_build_props_src_relpath = self.release_info["msvc"]["msbuild"].get("directory-build-props") for project in projects: dir_b_props = project.parent / "Directory.Build.props" dir_b_props.unlink(missing_ok = True) if directory_build_props_src_relpath: src = self.root / directory_build_props_src_relpath logger.debug("Copying %s -> %s", src, dir_b_props) shutil.copy(src=src, dst=dir_b_props) with self.section_printer.group(f"Build {arch_platform.arch} VS binary"): vs.build(arch_platform=arch_platform, projects=projects) if self.dry: for b in built_paths: b.parent.mkdir(parents=True, exist_ok=True) b.touch() for b in built_paths: assert b.is_file(), f"{b} has not been created" b.parent.mkdir(parents=True, exist_ok=True) b.touch() zip_path = self.dist_path / f"{self.project}-{self.version}-win32-{arch_platform.arch}.zip" zip_path.unlink(missing_ok=True) logger.info("Collecting files...") archive_file_tree = ArchiveFileTree() archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["msvc"]["msbuild"]["files-lib"], file_mapping_root=self.root, context=platform_context, time=self.arc_time) archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["msvc"]["files-lib"], file_mapping_root=self.root, context=platform_context, time=self.arc_time) logger.info("Writing to %s", zip_path) with Archiver(zip_path=zip_path) as archiver: arc_root = f"" archive_file_tree.add_to_archiver(archive_base=arc_root, archiver=archiver) archiver.add_git_hash(arcdir=arc_root, commit=self.commit, time=self.arc_time) self.artifacts[f"VC-{arch_platform.arch}"] = zip_path for p in built_paths: assert p.is_file(), f"{p} should exist" def _arch_platform_to_build_path(self, arch_platform: VsArchPlatformConfig) -> Path: return self.root / f"build-vs-{arch_platform.arch}" def _arch_platform_to_install_path(self, arch_platform: VsArchPlatformConfig) -> Path: return self._arch_platform_to_build_path(arch_platform) / "prefix" def _build_msvc_cmake(self, arch_platform: VsArchPlatformConfig, dep_roots: list[Path]): build_path = self._arch_platform_to_build_path(arch_platform) install_path = self._arch_platform_to_install_path(arch_platform) platform_context = self.get_context(extra_context=arch_platform.extra_context()) build_type = "Release" built_paths = set(install_path / configure_text(f, context=platform_context) for file_mapping in (self.release_info["msvc"]["cmake"]["files-lib"], self.release_info["msvc"]["cmake"]["files-devel"]) for files_list in file_mapping.values() for f in files_list) logger.info("CMake builds these files, to be included in the package: %s", built_paths) if not self.fast: for b in built_paths: b.unlink(missing_ok=True) shutil.rmtree(install_path, ignore_errors=True) build_path.mkdir(parents=True, exist_ok=True) with self.section_printer.group(f"Configure VC CMake project for {arch_platform.arch}"): self.executer.run([ "cmake", "-S", str(self.root), "-B", str(build_path), "-A", arch_platform.platform, "-DCMAKE_INSTALL_BINDIR=bin", "-DCMAKE_INSTALL_DATAROOTDIR=share", "-DCMAKE_INSTALL_INCLUDEDIR=include", "-DCMAKE_INSTALL_LIBDIR=lib", f"-DCMAKE_BUILD_TYPE={build_type}", f"-DCMAKE_INSTALL_PREFIX={install_path}", # MSVC debug information format flags are selected by an abstraction "-DCMAKE_POLICY_DEFAULT_CMP0141=NEW", # MSVC debug information format "-DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=ProgramDatabase", # Linker flags for executables "-DCMAKE_EXE_LINKER_FLAGS=-INCREMENTAL:NO -DEBUG -OPT:REF -OPT:ICF", # Linker flag for shared libraries "-DCMAKE_SHARED_LINKER_FLAGS=-INCREMENTAL:NO -DEBUG -OPT:REF -OPT:ICF", # MSVC runtime library flags are selected by an abstraction "-DCMAKE_POLICY_DEFAULT_CMP0091=NEW", # Use statically linked runtime (-MT) (ideally, should be "MultiThreaded$<$:Debug>") "-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded", f"-DCMAKE_PREFIX_PATH={';'.join(str(s) for s in dep_roots)}", ] + self.release_info["msvc"]["cmake"]["args"] + ([] if self.fast else ["--fresh"])) with self.section_printer.group(f"Build VC CMake project for {arch_platform.arch}"): self.executer.run(["cmake", "--build", str(build_path), "--verbose", "--config", build_type]) with self.section_printer.group(f"Install VC CMake project for {arch_platform.arch}"): self.executer.run(["cmake", "--install", str(build_path), "--config", build_type]) if self.dry: for b in built_paths: b.parent.mkdir(parents=True, exist_ok=True) b.touch() zip_path = self.dist_path / f"{self.project}-{self.version}-win32-{arch_platform.arch}.zip" zip_path.unlink(missing_ok=True) logger.info("Collecting files...") archive_file_tree = ArchiveFileTree() archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["msvc"]["cmake"]["files-lib"], file_mapping_root=install_path, context=platform_context, time=self.arc_time) archive_file_tree.add_file_mapping(arc_dir="", file_mapping=self.release_info["msvc"]["files-lib"], file_mapping_root=self.root, context=self.get_context(), time=self.arc_time) logger.info("Creating %s", zip_path) with Archiver(zip_path=zip_path) as archiver: arc_root = f"" archive_file_tree.add_to_archiver(archive_base=arc_root, archiver=archiver) archiver.add_git_hash(arcdir=arc_root, commit=self.commit, time=self.arc_time) for p in built_paths: assert p.is_file(), f"{p} should exist" def _build_msvc_devel(self) -> None: zip_path = self.dist_path / f"{self.project}-devel-{self.version}-VC.zip" arc_root = f"{self.project}-{self.version}" def copy_files_devel(ctx): archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["msvc"]["files-devel"], file_mapping_root=self.root, context=ctx, time=self.arc_time) logger.info("Collecting files...") archive_file_tree = ArchiveFileTree() if "msbuild" in self.release_info["msvc"]: for arch in self.release_info["msvc"]["msbuild"]["archs"]: arch_platform = self._arch_to_vs_platform(arch=arch) platform_context = self.get_context(arch_platform.extra_context()) archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["msvc"]["msbuild"]["files-devel"], file_mapping_root=self.root, context=platform_context, time=self.arc_time) copy_files_devel(ctx=platform_context) if "cmake" in self.release_info["msvc"]: for arch in self.release_info["msvc"]["cmake"]["archs"]: arch_platform = self._arch_to_vs_platform(arch=arch) platform_context = self.get_context(arch_platform.extra_context()) archive_file_tree.add_file_mapping(arc_dir=arc_root, file_mapping=self.release_info["msvc"]["cmake"]["files-devel"], file_mapping_root=self._arch_platform_to_install_path(arch_platform), context=platform_context, time=self.arc_time) copy_files_devel(ctx=platform_context) with Archiver(zip_path=zip_path) as archiver: archive_file_tree.add_to_archiver(archive_base="", archiver=archiver) archiver.add_git_hash(arcdir=arc_root, commit=self.commit, time=self.arc_time) self.artifacts["VC-devel"] = zip_path @classmethod def extract_sdl_version(cls, root: Path, release_info: dict) -> str: with open(root / release_info["version"]["file"], "r") as f: text = f.read() major = next(re.finditer(release_info["version"]["re_major"], text, flags=re.M)).group(1) minor = next(re.finditer(release_info["version"]["re_minor"], text, flags=re.M)).group(1) micro = next(re.finditer(release_info["version"]["re_micro"], text, flags=re.M)).group(1) return f"{major}.{minor}.{micro}" def main(argv=None) -> int: if sys.version_info < (3, 11): logger.error("This script needs at least python 3.11") return 1 parser = argparse.ArgumentParser(allow_abbrev=False, description="Create SDL release artifacts") parser.add_argument("--root", metavar="DIR", type=Path, default=Path(__file__).absolute().parents[1], help="Root of project") parser.add_argument("--release-info", metavar="JSON", dest="path_release_info", type=Path, default=Path(__file__).absolute().parent / "release-info.json", help="Path of release-info.json") parser.add_argument("--dependency-folder", metavar="FOLDER", dest="deps_path", type=Path, default="deps", help="Directory containing pre-built archives of dependencies (will be removed when downloading archives)") parser.add_argument("--out", "-o", metavar="DIR", dest="dist_path", type=Path, default="dist", help="Output directory") parser.add_argument("--github", action="store_true", help="Script is running on a GitHub runner") parser.add_argument("--commit", default="HEAD", help="Git commit/tag of which a release should be created") parser.add_argument("--actions", choices=["download", "source", "android", "mingw", "msvc", "dmg"], required=True, nargs="+", dest="actions", help="What to do?") parser.set_defaults(loglevel=logging.INFO) parser.add_argument('--vs-year', dest="vs_year", help="Visual Studio year") parser.add_argument('--android-api', type=int, dest="android_api", help="Android API version") parser.add_argument('--android-home', dest="android_home", default=os.environ.get("ANDROID_HOME"), help="Android Home folder") parser.add_argument('--android-ndk-home', dest="android_ndk_home", default=os.environ.get("ANDROID_NDK_HOME"), help="Android NDK Home folder") parser.add_argument('--cmake-generator', dest="cmake_generator", default="Ninja", help="CMake Generator") parser.add_argument('--debug', action='store_const', const=logging.DEBUG, dest="loglevel", help="Print script debug information") parser.add_argument('--dry-run', action='store_true', dest="dry", help="Don't execute anything") parser.add_argument('--force', action='store_true', dest="force", help="Ignore a non-clean git tree") parser.add_argument('--overwrite', action='store_true', dest="overwrite", help="Allow potentially overwriting other projects") parser.add_argument('--fast', action='store_true', dest="fast", help="Don't do a rebuild") args = parser.parse_args(argv) logging.basicConfig(level=args.loglevel, format='[%(levelname)s] %(message)s') args.deps_path = args.deps_path.absolute() args.dist_path = args.dist_path.absolute() args.root = args.root.absolute() args.dist_path = args.dist_path.absolute() if args.dry: args.dist_path = args.dist_path / "dry" if args.github: section_printer: SectionPrinter = GitHubSectionPrinter() else: section_printer = SectionPrinter() if args.github and "GITHUB_OUTPUT" not in os.environ: os.environ["GITHUB_OUTPUT"] = "/tmp/github_output.txt" executer = Executer(root=args.root, dry=args.dry) root_git_hash_path = args.root / GIT_HASH_FILENAME root_is_maybe_archive = root_git_hash_path.is_file() if root_is_maybe_archive: logger.warning("%s detected: Building from archive", GIT_HASH_FILENAME) archive_commit = root_git_hash_path.read_text().strip() if args.commit != archive_commit: logger.warning("Commit argument is %s, but archive commit is %s. Using %s.", args.commit, archive_commit, archive_commit) args.commit = archive_commit revision = (args.root / REVISION_TXT).read_text().strip() else: args.commit = executer.check_output(["git", "rev-parse", args.commit], dry_out="e5812a9fd2cda317b503325a702ba3c1c37861d9").strip() revision = executer.check_output(["git", "describe", "--always", "--tags", "--long", args.commit], dry_out="preview-3.1.3-96-g9512f2144").strip() logger.info("Using commit %s", args.commit) try: with args.path_release_info.open() as f: release_info = json.load(f) except FileNotFoundError: logger.error(f"Could not find {args.path_release_info}") releaser = Releaser( release_info=release_info, commit=args.commit, revision=revision, root=args.root, dist_path=args.dist_path, executer=executer, section_printer=section_printer, cmake_generator=args.cmake_generator, deps_path=args.deps_path, overwrite=args.overwrite, github=args.github, fast=args.fast, ) if root_is_maybe_archive: logger.warning("Building from archive. Skipping clean git tree check.") else: porcelain_status = executer.check_output(["git", "status", "--ignored", "--porcelain"], dry_out="\n").strip() if porcelain_status: print(porcelain_status) logger.warning("The tree is dirty! Do not publish any generated artifacts!") if not args.force: raise Exception("The git repo contains modified and/or non-committed files. Run with --force to ignore.") if args.fast: logger.warning("Doing fast build! Do not publish generated artifacts!") with section_printer.group("Arguments"): print(f"project = {releaser.project}") print(f"version = {releaser.version}") print(f"revision = {revision}") print(f"commit = {args.commit}") print(f"out = {args.dist_path}") print(f"actions = {args.actions}") print(f"dry = {args.dry}") print(f"force = {args.force}") print(f"overwrite = {args.overwrite}") print(f"cmake_generator = {args.cmake_generator}") releaser.prepare() if "download" in args.actions: releaser.download_dependencies() if set(args.actions).intersection({"msvc", "mingw", "android"}): print("Verifying presence of dependencies (run 'download' action to download) ...") releaser.verify_dependencies() print("... done") if "source" in args.actions: if root_is_maybe_archive: raise Exception("Cannot build source archive from source archive") with section_printer.group("Create source archives"): releaser.create_source_archives() if "dmg" in args.actions: if platform.system() != "Darwin" and not args.dry: parser.error("framework artifact(s) can only be built on Darwin") releaser.create_dmg() if "msvc" in args.actions: if platform.system() != "Windows" and not args.dry: parser.error("msvc artifact(s) can only be built on Windows") releaser.build_msvc() if "mingw" in args.actions: releaser.create_mingw_archives() if "android" in args.actions: if args.android_home is None or not Path(args.android_home).is_dir(): parser.error("Invalid $ANDROID_HOME or --android-home: must be a directory containing the Android SDK") if args.android_ndk_home is None or not Path(args.android_ndk_home).is_dir(): parser.error("Invalid $ANDROID_NDK_HOME or --android_ndk_home: must be a directory containing the Android NDK") if args.android_api is None: with section_printer.group("Detect Android APIS"): args.android_api = releaser._detect_android_api(android_home=args.android_home) if args.android_api is None or not (Path(args.android_home) / f"platforms/android-{args.android_api}").is_dir(): parser.error("Invalid --android-api, and/or could not be detected") with section_printer.group("Android arguments"): print(f"android_home = {args.android_home}") print(f"android_ndk_home = {args.android_ndk_home}") print(f"android_api = {args.android_api}") releaser.create_android_archives( android_api=args.android_api, android_home=args.android_home, android_ndk_home=args.android_ndk_home, ) with section_printer.group("Summary"): print(f"artifacts = {releaser.artifacts}") if args.github: with open(os.environ["GITHUB_OUTPUT"], "a") as f: f.write(f"project={releaser.project}\n") f.write(f"version={releaser.version}\n") for k, v in releaser.artifacts.items(): f.write(f"{k}={v.name}\n") return 0 if __name__ == "__main__": raise SystemExit(main()) SDL2_image-2.8.8/build-scripts/cmake-toolchain-mingw64-i686.cmake0000664000000000000000000000103314727103713021140 0ustar00set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_SYSTEM_PROCESSOR x86) find_program(CMAKE_C_COMPILER NAMES i686-w64-mingw32-gcc) find_program(CMAKE_CXX_COMPILER NAMES i686-w64-mingw32-g++) find_program(CMAKE_RC_COMPILER NAMES i686-w64-mingw32-windres windres) if(NOT CMAKE_C_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_C_COMPILER.") endif() if(NOT CMAKE_CXX_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_CXX_COMPILER.") endif() if(NOT CMAKE_RC_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_RC_COMPILER.") endif() SDL2_image-2.8.8/build-scripts/cmake-toolchain-mingw64-x86_64.cmake0000664000000000000000000000104414727103713021404 0ustar00set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_SYSTEM_PROCESSOR x86_64) find_program(CMAKE_C_COMPILER NAMES x86_64-w64-mingw32-gcc) find_program(CMAKE_CXX_COMPILER NAMES x86_64-w64-mingw32-g++) find_program(CMAKE_RC_COMPILER NAMES x86_64-w64-mingw32-windres windres) if(NOT CMAKE_C_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_C_COMPILER.") endif() if(NOT CMAKE_CXX_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_CXX_COMPILER.") endif() if(NOT CMAKE_RC_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_RC_COMPILER.") endif() SDL2_image-2.8.8/build-scripts/create-release.py0000775000000000000000000000272114727103713016360 0ustar00#!/usr/bin/env python3 import argparse from pathlib import Path import json import logging import re import subprocess ROOT = Path(__file__).resolve().parents[1] def determine_remote() -> str: text = (ROOT / "build-scripts/release-info.json").read_text() release_info = json.loads(text) if "remote" in release_info: return release_info["remote"] project_with_version = release_info["name"] project, _ = re.subn("([^a-zA-Z_])", "", project_with_version) return f"libsdl-org/{project}" def main(): default_remote = determine_remote() parser = argparse.ArgumentParser(allow_abbrev=False) parser.add_argument("--ref", required=True, help=f"Name of branch or tag containing release.yml") parser.add_argument("--remote", "-R", default=default_remote, help=f"Remote repo (default={default_remote})") parser.add_argument("--commit", help=f"Input 'commit' of release.yml (default is the hash of the ref)") args = parser.parse_args() if args.commit is None: args.commit = subprocess.check_output(["git", "rev-parse", args.ref], cwd=ROOT, text=True).strip() print(f"Running release.yml workflow:") print(f" remote = {args.remote}") print(f" ref = {args.ref}") print(f" commit = {args.commit}") subprocess.check_call(["gh", "-R", args.remote, "workflow", "run", "release.yml", "--ref", args.ref, "-f", f"commit={args.commit}"], cwd=ROOT) if __name__ == "__main__": raise SystemExit(main()) SDL2_image-2.8.8/build-scripts/release-info.json0000664000000000000000000001106514730273057016372 0ustar00{ "name": "SDL2_image", "remote": "libsdl-org/SDL_image", "dependencies": { "SDL": { "startswith": "2.", "repo": "libsdl-org/SDL" } }, "version": { "file": "include/SDL_image.h", "re_major": "^#define SDL_IMAGE_MAJOR_VERSION\\s+([0-9]+)$", "re_minor": "^#define SDL_IMAGE_MINOR_VERSION\\s+([0-9]+)$", "re_micro": "^#define SDL_IMAGE_PATCHLEVEL\\s+([0-9]+)$" }, "source": { "checks": [ "include/SDL_image.h", "src/IMG.c", "src/IMG_tif.c" ] }, "dmg": { "project": "Xcode/SDL_image.xcodeproj", "path": "Xcode/build/SDL2_image.dmg", "scheme": "Create DMG", "build-xcconfig": "Xcode/pkg-support/build.xcconfig", "dependencies": { "SDL": { "artifact": "SDL2-*.dmg" } } }, "mingw": { "autotools": { "archs": ["x86", "x64"], "args": [ "--with-sdl-prefix=@<@DEP_PREFIX@>@", "CFLAGS=-O2 -I@<@DEP_PREFIX@>@/include -I@<@DEP_PREFIX@>@/include/SDL2", "LDFLAGS=-Wl,-s -L@<@PROJECT_ROOT@>@/VisualC/external/optional/@<@ARCH@>@ -L@<@DEP_PREFIX@>@/lib -lSDL2", "LIBAVIF_CFLAGS=-I@<@PROJECT_ROOT@>@/VisualC/external/include", "LIBAVIF_LIBS=-L@<@PROJECT_ROOT@>@/VisualC/external/optional/@<@ARCH@>@", "LIBTIFF_CFLAGS=-I@<@PROJECT_ROOT@>@/VisualC/external/include", "LIBTIFF_LIBS=-L@<@PROJECT_ROOT@>@/VisualC/external/optional/@<@ARCH@>@", "LIBWEBPDEMUX_CFLAGS=-I@<@PROJECT_ROOT@>@/VisualC/external/include", "LIBWEBPDEMUX_LIBS=-L@<@PROJECT_ROOT@>@/VisualC/external/optional/@<@ARCH@>@", "LIBWEBP_CFLAGS=-I@<@PROJECT_ROOT@>@/VisualC/external/include", "LIBWEBP_LIBS=-L@<@PROJECT_ROOT@>@/VisualC/external/optional/@<@ARCH@>@" ] }, "files": { "": [ "CHANGES.txt", "LICENSE.txt", "README.txt", "mingw/pkg-support/Makefile" ], "cmake": [ "mingw/pkg-support/cmake/sdl2_image-config.cmake", "mingw/pkg-support/cmake/sdl2_image-config-version.cmake" ] }, "dependencies": { "SDL": { "artifact": "SDL2-devel-*-mingw.tar.gz", "install-command": "make install-package arch=@<@TRIPLET@>@ prefix=@<@PREFIX@>@" } } }, "msvc": { "msbuild": { "archs": [ "x86", "x64" ], "projects": [ "VisualC/SDL_image.vcxproj" ], "files-lib": { "": [ "VisualC/@<@PLATFORM@>@/@<@CONFIGURATION@>@/SDL2_image.dll" ] }, "files-devel": { "lib/@<@ARCH@>@": [ "VisualC/@<@PLATFORM@>@/@<@CONFIGURATION@>@/SDL2_image.dll", "VisualC/@<@PLATFORM@>@/@<@CONFIGURATION@>@/SDL2_image.lib", "VisualC/@<@PLATFORM@>@/@<@CONFIGURATION@>@/SDL2_image.pdb" ] } }, "files-lib": { "": [ "README.txt" ], "optional": [ "VisualC/external/optional/@<@ARCH@>@/libavif-16.dll", "VisualC/external/optional/@<@ARCH@>@/libtiff-5.dll", "VisualC/external/optional/@<@ARCH@>@/libwebp-7.dll", "VisualC/external/optional/@<@ARCH@>@/libwebpdemux-2.dll", "VisualC/external/optional/@<@ARCH@>@/LICENSE.avif.txt", "VisualC/external/optional/@<@ARCH@>@/LICENSE.dav1d.txt", "VisualC/external/optional/@<@ARCH@>@/LICENSE.tiff.txt", "VisualC/external/optional/@<@ARCH@>@/LICENSE.webp.txt" ] }, "files-devel": { "": [ "CHANGES.txt", "LICENSE.txt", "README.txt" ], "include": [ "include/SDL_image.h" ], "lib/@<@ARCH@>@/optional": [ "VisualC/external/optional/@<@ARCH@>@/libavif-16.dll", "VisualC/external/optional/@<@ARCH@>@/libtiff-5.dll", "VisualC/external/optional/@<@ARCH@>@/libwebp-7.dll", "VisualC/external/optional/@<@ARCH@>@/libwebpdemux-2.dll", "VisualC/external/optional/@<@ARCH@>@/LICENSE.avif.txt", "VisualC/external/optional/@<@ARCH@>@/LICENSE.dav1d.txt", "VisualC/external/optional/@<@ARCH@>@/LICENSE.tiff.txt", "VisualC/external/optional/@<@ARCH@>@/LICENSE.webp.txt" ], "cmake": [ "VisualC/pkg-support/cmake/sdl2_image-config.cmake", "VisualC/pkg-support/cmake/sdl2_image-config-version.cmake" ] }, "dependencies": { "SDL": { "artifact": "SDL2-devel-*-VC.zip", "copy": [ { "src": "lib/@<@ARCH@>@/SDL2.*", "dst": "../SDL/VisualC/@<@PLATFORM@>@/@<@CONFIGURATION@>@" }, { "src": "include/*", "dst": "../SDL/include" } ] } } } } SDL2_image-2.8.8/build-scripts/test-versioning.sh0000775000000000000000000001362714751444477016644 0ustar00#!/bin/sh # Copyright 2022 Collabora Ltd. # SPDX-License-Identifier: Zlib set -eu cd `dirname $0`/.. # Needed so sed doesn't report illegal byte sequences on macOS export LC_CTYPE=C header=include/SDL_image.h ref_major=$(sed -ne 's/^#define SDL_IMAGE_MAJOR_VERSION *//p' $header) ref_minor=$(sed -ne 's/^#define SDL_IMAGE_MINOR_VERSION *//p' $header) ref_micro=$(sed -ne 's/^#define SDL_IMAGE_PATCHLEVEL *//p' $header) ref_version="${ref_major}.${ref_minor}.${ref_micro}" tests=0 failed=0 ok () { tests=$(( tests + 1 )) echo "ok - $*" } not_ok () { tests=$(( tests + 1 )) echo "not ok - $*" failed=1 } major=$(sed -Ene 's/^m4_define\(\[MAJOR_VERSION_MACRO\], \[([0-9]*)\]\)$/\1/p' configure.ac) minor=$(sed -Ene 's/^m4_define\(\[MINOR_VERSION_MACRO\], \[([0-9]*)\]\)$/\1/p' configure.ac) micro=$(sed -Ene 's/^m4_define\(\[MICRO_VERSION_MACRO\], \[([0-9]*)\]\)$/\1/p' configure.ac) version="${major}.${minor}.${micro}" ref_sdl_req=$(sed -ne 's/^SDL_VERSION=//p' configure.ac) if [ "$ref_version" = "$version" ]; then ok "configure.ac $version" else not_ok "configure.ac $version disagrees with $header $ref_version" fi major=$(sed -ne 's/^MAJOR_VERSION=//p' configure) minor=$(sed -ne 's/^MINOR_VERSION=//p' configure) micro=$(sed -ne 's/^MICRO_VERSION=//p' configure) version="${major}.${minor}.${micro}" if [ "$ref_version" = "$version" ]; then ok "configure $version" else not_ok "configure $version disagrees with $header $ref_version" fi major=$(sed -ne 's/^set(MAJOR_VERSION \([0-9]*\))$/\1/p' CMakeLists.txt) minor=$(sed -ne 's/^set(MINOR_VERSION \([0-9]*\))$/\1/p' CMakeLists.txt) micro=$(sed -ne 's/^set(MICRO_VERSION \([0-9]*\))$/\1/p' CMakeLists.txt) sdl_req=$(sed -ne 's/^set(SDL_REQUIRED_VERSION \([0-9.]*\))$/\1/p' CMakeLists.txt) version="${major}.${minor}.${micro}" if [ "$ref_version" = "$version" ]; then ok "CMakeLists.txt $version" else not_ok "CMakeLists.txt $version disagrees with $header $ref_version" fi if [ "$ref_sdl_req" = "$sdl_req" ]; then ok "CMakeLists.txt $sdl_req" else not_ok "CMakeLists.txt SDL_REQUIRED_VERSION=$sdl_req disagrees with configure.ac SDL_VERSION=$ref_sdl_req" fi major=$(sed -ne 's/^MAJOR_VERSION *= *//p' Makefile.os2) minor=$(sed -ne 's/^MINOR_VERSION *= *//p' Makefile.os2) micro=$(sed -ne 's/^MICRO_VERSION *= *//p' Makefile.os2) version="${major}.${minor}.${micro}" if [ "$ref_version" = "$version" ]; then ok "Makefile.os2 $version" else not_ok "Makefile.os2 $version disagrees with $header $ref_version" fi for rcfile in src/version.rc VisualC/Version.rc; do tuple=$(sed -ne 's/^ *FILEVERSION *//p' "$rcfile" | tr -d '\r') ref_tuple="${ref_major},${ref_minor},${ref_micro},0" if [ "$ref_tuple" = "$tuple" ]; then ok "$rcfile FILEVERSION $tuple" else not_ok "$rcfile FILEVERSION $tuple disagrees with $header $ref_tuple" fi tuple=$(sed -ne 's/^ *PRODUCTVERSION *//p' "$rcfile" | tr -d '\r') if [ "$ref_tuple" = "$tuple" ]; then ok "$rcfile PRODUCTVERSION $tuple" else not_ok "$rcfile PRODUCTVERSION $tuple disagrees with $header $ref_tuple" fi tuple=$(sed -Ene 's/^ *VALUE "FileVersion", "([0-9, ]*)\\0"\r?$/\1/p' "$rcfile" | tr -d '\r') ref_tuple="${ref_major}, ${ref_minor}, ${ref_micro}, 0" if [ "$ref_tuple" = "$tuple" ]; then ok "$rcfile FileVersion $tuple" else not_ok "$rcfile FileVersion $tuple disagrees with $header $ref_tuple" fi tuple=$(sed -Ene 's/^ *VALUE "ProductVersion", "([0-9, ]*)\\0"\r?$/\1/p' "$rcfile" | tr -d '\r') if [ "$ref_tuple" = "$tuple" ]; then ok "$rcfile ProductVersion $tuple" else not_ok "$rcfile ProductVersion $tuple disagrees with $header $ref_tuple" fi done version=$(sed -Ene '/CFBundleShortVersionString/,+1 s/.*(.*)<\/string>.*/\1/p' Xcode/Info-Framework.plist) if [ "$ref_version" = "$version" ]; then ok "Info-Framework.plist CFBundleShortVersionString $version" else not_ok "Info-Framework.plist CFBundleShortVersionString $version disagrees with $header $ref_version" fi version=$(sed -Ene '/CFBundleVersion/,+1 s/.*(.*)<\/string>.*/\1/p' Xcode/Info-Framework.plist) if [ "$ref_version" = "$version" ]; then ok "Info-Framework.plist CFBundleVersion $version" else not_ok "Info-Framework.plist CFBundleVersion $version disagrees with $header $ref_version" fi # For simplicity this assumes we'll never break ABI before SDL 3. dylib_compat=$(sed -Ene 's/.*DYLIB_COMPATIBILITY_VERSION = (.*);$/\1/p' Xcode/SDL_image.xcodeproj/project.pbxproj) case "$ref_minor" in (*[02468]) major="$(( ref_minor * 100 + 1 ))" minor="0" ;; (*) major="$(( ref_minor * 100 + ref_micro + 1 ))" minor="0" ;; esac ref="${major}.${minor}.0 ${major}.${minor}.0" if [ "$ref" = "$dylib_compat" ]; then ok "project.pbxproj DYLIB_COMPATIBILITY_VERSION is consistent" else not_ok "project.pbxproj DYLIB_COMPATIBILITY_VERSION is inconsistent, expected $ref, got $dylib_compat" fi dylib_cur=$(sed -Ene 's/.*DYLIB_CURRENT_VERSION = (.*);$/\1/p' Xcode/SDL_image.xcodeproj/project.pbxproj) case "$ref_minor" in (*[02468]) major="$(( ref_minor * 100 + 1 ))" minor="$ref_micro" ;; (*) major="$(( ref_minor * 100 + ref_micro + 1 ))" minor="0" ;; esac ref="${major}.${minor}.0 ${major}.${minor}.0" if [ "$ref" = "$dylib_cur" ]; then ok "project.pbxproj DYLIB_CURRENT_VERSION is consistent" else not_ok "project.pbxproj DYLIB_CURRENT_VERSION is inconsistent, expected $ref, got $dylib_cur" fi if [ -f .github/fetch_sdl_vc.ps1 ]; then sdl_req=$(sed -ne 's/\$sdl2_version = "\([0-9.]*\)"$/\1/p' .github/fetch_sdl_vc.ps1) if [ "$ref_sdl_req" = "$sdl_req" ]; then ok ".github/fetch_sdl_vc.ps1 $sdl_req" else not_ok ".github/fetch_sdl_vc.ps1 sdl2_version=$sdl_req disagrees with configure.ac SDL_VERSION=$ref_sdl_req" fi fi echo "1..$tests" exit "$failed" SDL2_image-2.8.8/build-scripts/touch-autofoo.sh0000775000000000000000000000035714751444473016270 0ustar00#!/bin/sh # Run this script from the root of $srcdir to touch the # autotools generated files, so that the build procedure # doesn't attempt to regenerate them. cd `dirname $0`/.. touch aclocal.m4 configure Makefile.in test/Makefile.in SDL2_image-2.8.8/cmake/CommonFindSDL2.cmake0000664000000000000000000000121714730005212015062 0ustar00# Common variables for FindSDL2*.cmake modules set(_inc_suffixes include) set(_lib_suffixes) if(MSVC) if(CMAKE_SIZEOF_VOID_P EQUAL 4) list(APPEND _lib_suffixes "lib/x86") endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND _lib_suffixes "lib/x64") endif() endif() if(MINGW) if(CMAKE_SIZEOF_VOID_P EQUAL 4) list(APPEND _lib_suffixes "i686-w64-mingw32/lib") list(APPEND _inc_suffixes "i686-w64-mingw32/include") endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND _lib_suffixes "x86_64-w64-mingw32/lib") list(APPEND _inc_suffixes "x86_64-w64-mingw32/include") endif() endif() SDL2_image-2.8.8/cmake/FindPrivateSDL2.cmake0000664000000000000000000000311014243476016015253 0ustar00# FIXME: this should be provided by SDL2 include(FindPackageHandleStandardArgs) include("${CMAKE_CURRENT_LIST_DIR}/CommonFindSDL2.cmake") find_library(SDL2_LIBRARY NAMES SDL2 HINTS ${SDL2_DIR} ENV SDL2_DIR PATH_SUFFIXES ${_lib_suffixes} ) find_path(SDL2_INCLUDE_DIR NAMES SDL_haptic.h PATH_SUFFIXES SDL2 HINTS ${SDL2_DIR} ENV SDL2_DIR PATH_SUFFIXES ${_inc_suffixes} ) set(SDL2_VERSION) if(SDL2_INCLUDE_DIR) file(READ "${SDL2_INCLUDE_DIR}/SDL_version.h" _sdl_version_h) string(REGEX MATCH "#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)" _sdl2_major_re "${_sdl_version_h}") set(_sdl2_major "${CMAKE_MATCH_1}") string(REGEX MATCH "#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)" _sdl2_minor_re "${_sdl_version_h}") set(_sdl2_minor "${CMAKE_MATCH_1}") string(REGEX MATCH "#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)" _sdl2_patch_re "${_sdl_version_h}") set(_sdl2_patch "${CMAKE_MATCH_1}") if(_sdl2_major_re AND _sdl2_minor_re AND _sdl2_patch_re) set(SDL2_VERSION "${_sdl2_major}.${_sdl2_minor}.${_sdl2_patch}") endif() endif() find_package_handle_standard_args(PrivateSDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR VERSION_VAR SDL2_VERSION ) if(PrivateSDL2_FOUND) if(NOT TARGET PrivateSDL2::PrivateSDL2) add_library(PrivateSDL2::PrivateSDL2 UNKNOWN IMPORTED) set_target_properties(PrivateSDL2::PrivateSDL2 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}" IMPORTED_LINK_INTERFACE_LANGUAGES "C" IMPORTED_LOCATION "${SDL2_LIBRARY}" ) endif() endif() SDL2_image-2.8.8/cmake/FindSDL2main.cmake0000664000000000000000000000120014243455757014574 0ustar00# FIXME: this should be provided by SDL2 include(FindPackageHandleStandardArgs) include("${CMAKE_CURRENT_LIST_DIR}/CommonFindSDL2.cmake") find_library(SDL2_MAIN_LIBRARY NAMES SDL2main HINTS ${SDL2_DIR} ENV SDL2_DIR PATH_SUFFIXES ${_lib_suffixes} ) find_package_handle_standard_args(SDL2main REQUIRED_VARS SDL2_MAIN_LIBRARY ) if(SDL2main_FOUND) if(NOT TARGET SDL2::SDL2main) add_library(SDL2::SDL2main UNKNOWN IMPORTED) set_target_properties(SDL2::SDL2main PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" IMPORTED_LOCATION "${SDL2_MAIN_LIBRARY}" ) endif() endif() SDL2_image-2.8.8/cmake/FindSDL2test.cmake0000664000000000000000000000211414243455757014634 0ustar00# FIXME: this should be provided by SDL2 include(FindPackageHandleStandardArgs) include("${CMAKE_CURRENT_LIST_DIR}/CommonFindSDL2.cmake") find_library(SDL2_TEST_LIBRARY NAMES SDL2test SDL2_test HINTS ${SDL2_DIR} ENV SDL2_DIR PATH_SUFFIXES ${_lib_suffixes} ) find_package_handle_standard_args(SDL2test REQUIRED_VARS SDL2_TEST_LIBRARY ) if(SDL2test_FOUND) if(NOT TARGET SDL2::SDL2test) add_library(SDL2::SDL2test UNKNOWN IMPORTED) set_target_properties(SDL2::SDL2test PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" IMPORTED_LOCATION "${SDL2_TEST_LIBRARY}" ) if(MSVC AND ((SDL2_VERSION AND SDL2_VERSION VERSION_LESS "2.0.20") OR NOT SDL2_VERSION)) # FIXME: remove once minimum required SDL library is >=2.0.20 # Until 2.0.18, SDL2test.lib used `printf` in SDL_test_common.c. instead of `SDL_log`. (fixed in 2.0.20) set_target_properties(SDL2::SDL2test PROPERTIES INTERFACE_LINK_LIBRARIES "legacy_stdio_definitions.lib" ) endif() endif() endif() SDL2_image-2.8.8/cmake/Findlibjxl.cmake0000664000000000000000000000167614243455757014530 0ustar00include(FindPackageHandleStandardArgs) find_library(libjxl_LIBRARY NAMES jxl ) find_path(libjxl_INCLUDE_PATH NAMES jxl/decode.h ) set(libjxl_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of libjxl") set(libjxl_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of libjxl") set(libjxl_LINK_FLAGS "" CACHE STRING "Extra link flags of libjxl") find_package_handle_standard_args(libjxl REQUIRED_VARS libjxl_LIBRARY libjxl_INCLUDE_PATH ) if (libjxl_FOUND) if (NOT TARGET libjxl::libjxl) add_library(libjxl::libjxl UNKNOWN IMPORTED) set_target_properties(libjxl::libjxl PROPERTIES IMPORTED_LOCATION "${libjxl_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${libjxl_INCLUDE_PATH}" INTERFACE_COMPILE_OPTIONS "${libjxl_COMPILE_OPTIONS}" INTERFACE_LINK_LIBRARIES "${libjxl_LINK_LIBRARIES}" INTERFACE_LINK_FLAGS "${libjxl_LINK_FLAGS}" ) endif() endif() SDL2_image-2.8.8/cmake/Findwebp.cmake0000664000000000000000000000340514730005212014143 0ustar00include(FindPackageHandleStandardArgs) find_library(webp_LIBRARY NAMES webp ) find_path(webp_INCLUDE_PATH NAMES webp/decode.h ) set(webp_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of webp") set(webp_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of webp") set(webp_LINK_FLAGS "" CACHE STRING "Extra link flags of webp") find_library(webpdemux_LIBRARY NAMES webpdemux ) find_path(webpdemux_INCLUDE_PATH NAMES webp/demux.h ) set(webpdemux_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of webpdemux") set(webpdemux_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of webpdemux") set(webpdemux_LINK_FLAGS "" CACHE STRING "Extra link flags of webpdemux") find_package_handle_standard_args(webp REQUIRED_VARS webp_LIBRARY webp_INCLUDE_PATH webpdemux_LIBRARY webpdemux_INCLUDE_PATH ) if (webp_FOUND) if (NOT TARGET WebP::webp) add_library(WebP::webp UNKNOWN IMPORTED) set_target_properties(WebP::webp PROPERTIES IMPORTED_LOCATION "${webp_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${webp_INCLUDE_PATH}" INTERFACE_COMPILE_OPTIONS "${webp_COMPILE_FLAGS}" INTERFACE_LINK_LIBRARIES "${webp_LINK_LIBRARIES}" INTERFACE_LINK_FLAGS "${webp_LINK_FLAGS}" ) endif() if (NOT TARGET WebP::webpdemux) add_library(WebP::webpdemux UNKNOWN IMPORTED) set_target_properties(WebP::webpdemux PROPERTIES IMPORTED_LOCATION "${webpdemux_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${webpdemux_INCLUDE_PATH}" INTERFACE_COMPILE_OPTIONS "${webpdemux_COMPILE_FLAGS}" INTERFACE_LINK_LIBRARIES "${webwebpdemux_LINK_LIBRARIES}" INTERFACE_LINK_FLAGS "${webpdemux_LINK_FLAGS}" ) endif() endif() SDL2_image-2.8.8/cmake/PrivateSdlFunctions.cmake0000664000000000000000000003050714727366527016407 0ustar00# This file is shared amongst SDL_image/SDL_mixer/SDL_ttf macro(sdl_calculate_derived_version_variables) if(NOT DEFINED MAJOR_VERSION OR NOT DEFINED MINOR_VERSION OR NOT DEFINED MICRO_VERSION) message(FATAL_ERROR "MAJOR_VERSION, MINOR_VERSION and MICRO_VERSION need to be defined") endif() set(FULL_VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${MICRO_VERSION}") # Calculate a libtool-like version number math(EXPR BINARY_AGE "${MINOR_VERSION} * 100 + ${MICRO_VERSION}") math(EXPR IS_DEVELOPMENT "${MINOR_VERSION} % 2") if(IS_DEVELOPMENT) # Development branch, 2.5.1 -> libSDL2_XXXXX-2.0.so.0.501.0 set(INTERFACE_AGE 0) else() # Stable branch, 2.6.1 -> libSDL2_XXXXX-2.0.so.0.600.1 set(INTERFACE_AGE ${MICRO_VERSION}) endif() # Increment this if there is an incompatible change - but if that happens, # we should rename the library from SDL2 to SDL3, at which point this would # reset to 0 anyway. set(LT_MAJOR "0") math(EXPR LT_AGE "${BINARY_AGE} - ${INTERFACE_AGE}") math(EXPR LT_CURRENT "${LT_MAJOR} + ${LT_AGE}") set(LT_REVISION "${INTERFACE_AGE}") # For historical reasons, the library name redundantly includes the major # version twice: libSDL2_XXXXX-2.0.so.0. # TODO: in SDL 3, set the OUTPUT_NAME to plain SDL3_XXXXX, which will simplify # it to libSDL2_XXXXX.so.0 set(LT_RELEASE "2.0") set(LT_VERSION "${LT_MAJOR}.${LT_AGE}.${LT_REVISION}") # The following should match the versions in the Xcode project file. # Each version is 1 higher than you might expect, for compatibility # with libtool: macOS ABI versioning is 1-based, unlike other platforms # which are normally 0-based. math(EXPR DYLIB_CURRENT_VERSION_MAJOR "${LT_MAJOR} + ${LT_AGE} + 1") math(EXPR DYLIB_CURRENT_VERSION_MINOR "${LT_REVISION}") set(DYLIB_CURRENT_VERSION "${DYLIB_CURRENT_VERSION_MAJOR}.${DYLIB_CURRENT_VERSION_MINOR}.0") set(DYLIB_COMPATIBILITY_VERSION "${DYLIB_CURRENT_VERSION_MAJOR}.0.0") endmacro() macro(sdl_find_sdl2 TARGET VERSION) if(NOT TARGET ${TARGET}) # FIXME: can't add REQUIRED since not all SDL2 installs ship SDL2ConfigVersion.cmake (or sdl2-config-version.cmake) find_package(SDL2 ${VERSION} QUIET) endif() if(NOT TARGET ${TARGET}) # FIXME: can't add REQUIRED since not all SDL2 installs ship SDL2Config.cmake (or sdl2-config.cmake) find_package(SDL2 QUIET) if(SDL2_FOUND) message(WARNING "Could not verify SDL2 version. Assuming SDL2 has version of at least ${VERSION}.") endif() endif() # Use Private FindSDL2.cmake module to find SDL2 for installations where no SDL2Config.cmake is available, # or for those installations where no target is generated. if(NOT TARGET ${TARGET}) message(STATUS "Using private SDL2 find module") find_package(PrivateSDL2 ${VERSION} REQUIRED) add_library(${TARGET} INTERFACE IMPORTED) set_target_properties(${TARGET} PROPERTIES INTERFACE_LINK_LIBRARIES "PrivateSDL2::PrivateSDL2" ) endif() endmacro() function(read_absolute_symlink DEST PATH) file(READ_SYMLINK "${PATH}" p) if(NOT IS_ABSOLUTE "${p}") get_filename_component(pdir "${PATH}" DIRECTORY) set(p "${pdir}/${p}") endif() get_filename_component(p "${p}" ABSOLUTE) set("${DEST}" "${p}" PARENT_SCOPE) endfunction() function(win32_implib_identify_dll DEST IMPLIB) cmake_parse_arguments(ARGS "NOTFATAL" "" "" ${ARGN}) if(CMAKE_DLLTOOL) execute_process( COMMAND "${CMAKE_DLLTOOL}" --identify "${IMPLIB}" RESULT_VARIABLE retcode OUTPUT_VARIABLE stdout ERROR_VARIABLE stderr) if(NOT retcode EQUAL 0) if(NOT ARGS_NOTFATAL) message(FATAL_ERROR "${CMAKE_DLLTOOL} failed.") else() set("${DEST}" "${DEST}-NOTFOUND" PARENT_SCOPE) return() endif() endif() string(STRIP "${stdout}" result) set(${DEST} "${result}" PARENT_SCOPE) elseif(MSVC) get_filename_component(CMAKE_C_COMPILER_DIRECTORY "${CMAKE_C_COMPILER}" DIRECTORY CACHE) find_program(CMAKE_DUMPBIN NAMES dumpbin PATHS "${CMAKE_C_COMPILER_DIRECTORY}") if(CMAKE_DUMPBIN) execute_process( COMMAND "${CMAKE_DUMPBIN}" "-headers" "${IMPLIB}" RESULT_VARIABLE retcode OUTPUT_VARIABLE stdout ERROR_VARIABLE stderr) if(NOT retcode EQUAL 0) if(NOT ARGS_NOTFATAL) message(FATAL_ERROR "dumpbin failed.") else() set(${DEST} "${DEST}-NOTFOUND" PARENT_SCOPE) return() endif() endif() string(REGEX MATCH "DLL name[ ]+:[ ]+([^\n]+)\n" match "${stdout}") if(NOT match) if(NOT ARGS_NOTFATAL) message(FATAL_ERROR "dumpbin did not find any associated dll for ${IMPLIB}.") else() set(${DEST} "${DEST}-NOTFOUND" PARENT_SCOPE) return() endif() endif() set(result "${CMAKE_MATCH_1}") set(${DEST} "${result}" PARENT_SCOPE) else() message(FATAL_ERROR "Cannot find dumpbin, please set CMAKE_DUMPBIN cmake variable") endif() else() if(NOT ARGS_NOTFATAL) message(FATAL_ERROR "Don't know how to identify dll from import library. Set CMAKE_DLLTOOL (for mingw) or CMAKE_DUMPBIN (for MSVC)") else() set(${DEST} "${DEST}-NOTFOUND") endif() endif() endfunction() function(get_actual_target) set(dst "${ARGV0}") set(target "${${dst}}") get_target_property(alias "${target}" ALIASED_TARGET) while(alias) set(target "${alias}") get_target_property(alias "${target}" ALIASED_TARGET) endwhile() set("${dst}" "${target}" PARENT_SCOPE) endfunction() function(target_get_dynamic_library DEST TARGET) set(result) get_actual_target(TARGET) if(WIN32) # Use the target dll of the import library set(props_to_check IMPORTED_IMPLIB) if(CMAKE_BUILD_TYPE) list(APPEND props_to_check IMPORTED_IMPLIB_${CMAKE_BUILD_TYPE}) endif() list(APPEND props_to_check IMPORTED_LOCATION) if(CMAKE_BUILD_TYPE) list(APPEND props_to_check IMPORTED_LOCATION_${CMAKE_BUILD_TYPE}) endif() foreach (config_type ${CMAKE_CONFIGURATION_TYPES} RELEASE DEBUG RELWITHDEBINFO MINSIZEREL) list(APPEND props_to_check IMPORTED_IMPLIB_${config_type}) list(APPEND props_to_check IMPORTED_LOCATION_${config_type}) endforeach() foreach(prop_to_check ${props_to_check}) if(NOT result) get_target_property(propvalue "${TARGET}" ${prop_to_check}) if(propvalue AND EXISTS "${propvalue}") win32_implib_identify_dll(result "${propvalue}" NOTFATAL) endif() endif() endforeach() else() # 1. find the target library a file might be symbolic linking to # 2. find all other files in the same folder that symolic link to it # 3. sort all these files, and select the 1st item on Linux, and last on Macos set(location_properties IMPORTED_LOCATION) if(CMAKE_BUILD_TYPE) list(APPEND location_properties IMPORTED_LOCATION_${CMAKE_BUILD_TYPE}) endif() foreach (config_type ${CMAKE_CONFIGURATION_TYPES} RELEASE DEBUG RELWITHDEBINFO MINSIZEREL) list(APPEND location_properties IMPORTED_LOCATION_${config_type}) endforeach() if(APPLE) set(valid_shared_library_regex "\\.[0-9]+\\.dylib$") else() set(valid_shared_library_regex "\\.so\\.([0-9.]+)?[0-9]") endif() foreach(location_property ${location_properties}) if(NOT result) get_target_property(library_path "${TARGET}" ${location_property}) if(EXISTS "${library_path}") get_filename_component(library_path "${library_path}" ABSOLUTE) while (IS_SYMLINK "${library_path}") read_absolute_symlink(library_path "${library_path}") endwhile() get_filename_component(libdir "${library_path}" DIRECTORY) file(GLOB subfiles "${libdir}/*") set(similar_files "${library_path}") foreach(subfile ${subfiles}) if(IS_SYMLINK "${subfile}") read_absolute_symlink(subfile_target "${subfile}") while (IS_SYMLINK "${subfile_target}") read_absolute_symlink(subfile_target "${subfile_target}") endwhile() get_filename_component(subfile_target "${subfile_target}" ABSOLUTE) if(subfile_target STREQUAL library_path AND subfile MATCHES "${valid_shared_library_regex}") list(APPEND similar_files "${subfile}") endif() endif() endforeach() list(SORT similar_files) set(index 0) if(APPLE) list(LENGTH similar_files len) math(EXPR index "${len}-1") endif() list(GET similar_files ${index} item) get_filename_component(result "${item}" NAME) endif() endif() endforeach() endif() if(result) string(TOLOWER "${result}" result_lower) if(WIN32 OR OS2) if(NOT result_lower MATCHES ".*dll") message(FATAL_ERROR "\"${result}\" is not a .dll library") endif() elseif(APPLE) if(NOT result_lower MATCHES ".*dylib.*") message(FATAL_ERROR "\"${result}\" is not a .dylib shared library") endif() else() if(NOT result_lower MATCHES ".*so.*") message(FATAL_ERROR "\"${result}\" is not a .so shared library") endif() endif() else() get_target_property(target_type ${TARGET} TYPE) if(target_type MATCHES "SHARED_LIBRARY|MODULE_LIBRARY") # OK elseif(target_type MATCHES "STATIC_LIBRARY|OBJECT_LIBRARY|INTERFACE_LIBRARY|EXECUTABLE") message(SEND_ERROR "${TARGET} is not a shared library, but has type=${target_type}") else() message(WARNING "Unable to extract dynamic library from target=${TARGET}, type=${target_type}.") endif() # TARGET_SONAME_FILE is not allowed for DLL target platforms. if(WIN32) set(result "$") else() set(result "$") endif() endif() set(${DEST} ${result} PARENT_SCOPE) endfunction() macro(sdl_check_project_in_subfolder relative_subfolder name vendored_option) if(NOT EXISTS "${PROJECT_SOURCE_DIR}/${relative_subfolder}/CMakeLists.txt") message(FATAL_ERROR "No cmake project for ${name} found in ${relative_subfolder}.\n" "Run the download script in the external folder, or re-configure with -D${vendored_option}=OFF to use system packages.") endif() endmacro() macro(sdl_check_linker_flag flag var) # FIXME: Use CheckLinkerFlag module once cmake minimum version >= 3.18 include(CMakePushCheckState) include(CheckCSourceCompiles) cmake_push_check_state(RESET) set(CMAKE_REQUIRED_LINK_OPTIONS "${flag}") check_c_source_compiles("int main() { return 0; }" ${var}) cmake_pop_check_state() endmacro() function(sdl_target_link_options_no_undefined TARGET) if(NOT MSVC AND NOT CMAKE_SYSTEM_NAME MATCHES ".*OpenBSD.*") if(CMAKE_C_COMPILER_ID MATCHES "AppleClang") target_link_options(${TARGET} PRIVATE "-Wl,-undefined,error") else() sdl_check_linker_flag("-Wl,--no-undefined" HAVE_WL_NO_UNDEFINED) if(HAVE_WL_NO_UNDEFINED AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") AND WIN32)) target_link_options(${TARGET} PRIVATE "-Wl,--no-undefined") endif() endif() endif() endfunction() SDL2_image-2.8.8/cmake/test/CMakeLists.txt0000664000000000000000000000352714254075622015144 0ustar00# This cmake build script is meant for verifying the various CMake configuration script. cmake_minimum_required(VERSION 3.12) project(sdl_test LANGUAGES C) cmake_policy(SET CMP0074 NEW) # Override CMAKE_FIND_ROOT_PATH_MODE to allow search for SDL2 outside of sysroot set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE NEVER) include(FeatureSummary) option(TEST_SHARED "Test linking to shared SDL2_image library" ON) add_feature_info("TEST_SHARED" TEST_SHARED "Test linking with shared library") option(TEST_STATIC "Test linking to static SDL2_image library" ON) add_feature_info("TEST_STATIC" TEST_STATIC "Test linking with static library") if(TEST_SHARED) # FIXME: in the distant future, must become REQUIRED find_package(SDL2 CONFIG COMPONENTS SDL2) # FIXME: and the following should be removed if(NOT TARGET SDL2::SDL2) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/..") include(PrivateSdlFunctions) sdl_find_sdl2(SDL2::SDL2 2.0) endif() find_package(SDL2_image REQUIRED CONFIG) add_executable(main_shared main.c) target_link_libraries(main_shared PRIVATE SDL2::SDL2 SDL2_image::SDL2_image) endif() if(TEST_STATIC) # FIXME: in the distant future, must become REQUIRED find_package(SDL2 CONFIG COMPONENTS SDL2-static) # FIXME: and the following should be removed if(NOT TARGET SDL2::SDL2-static) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/..") include(PrivateSdlFunctions) sdl_find_sdl2(SDL2::SDL2-static 2.0) endif() # some static vendored libraries use c++ (enable CXX after `find_package` might show a warning) enable_language(CXX) find_package(SDL2_image REQUIRED CONFIG) add_executable(main_static main.c) target_link_libraries(main_static PRIVATE SDL2::SDL2-static SDL2_image::SDL2_image-static) endif() feature_summary(WHAT ALL) SDL2_image-2.8.8/cmake/test/main.c0000664000000000000000000000066214247536537013502 0ustar00#define SDL_MAIN_HANDLED #include "SDL.h" #include "SDL_image.h" #include int main(int argc, char *argv[]) { SDL_SetMainReady(); if (SDL_Init(0) < 0) { fprintf(stderr, "could not initialize sdl2: %s\n", SDL_GetError()); return 1; } SDL_SetMainReady(); if (IMG_Init(0) == 0) { fprintf(stderr, "no image formats supported\n"); } IMG_Quit(); SDL_Quit(); return 0; } SDL2_image-2.8.8/configure.ac0000664000000000000000000007124214761421756012640 0ustar00dnl Process this file with autoconf to produce a configure script. dnl Set various version strings - taken gratefully from the GTk sources # See docs/release_checklist.md m4_define([MAJOR_VERSION_MACRO], [2]) m4_define([MINOR_VERSION_MACRO], [8]) m4_define([MICRO_VERSION_MACRO], [8]) AC_INIT([SDL2_image], [MAJOR_VERSION_MACRO.MINOR_VERSION_MACRO.MICRO_VERSION_MACRO], [https://github.com/libsdl-org/SDL_image/issues], [SDL2_image]) AC_CONFIG_MACRO_DIR([acinclude]) AC_CONFIG_SRCDIR([src/IMG.c]) AC_SUBST([MAJOR_VERSION], MAJOR_VERSION_MACRO) AC_SUBST([MINOR_VERSION], MINOR_VERSION_MACRO) AC_SUBST([MICRO_VERSION], MICRO_VERSION_MACRO) BINARY_AGE=`expr $MINOR_VERSION \* 100 + $MICRO_VERSION` AS_CASE(["$MINOR_VERSION"], [*@<:@02468@:>@], dnl Stable branch, 2.6.1 -> libSDL2-2.0.so.0.600.1 [INTERFACE_AGE="$MICRO_VERSION"], [*], dnl Development branch, 2.5.1 -> libSDL2-2.0.so.0.501.0 [INTERFACE_AGE=0]) dnl libtool versioning LT_INIT([win32-dll]) # For historical reasons, the library name redundantly includes the major # version twice: libSDL2_image-2.0.so.0. # TODO: in SDL 3, stop using -release, which will simplify it to libSDL3.so.0 LT_RELEASE=2.0 # Increment this if there is an incompatible change - but if that happens, # we should rename the library from SDL2 to SDL3, at which point this would # reset to 0 anyway. LT_MAJOR=0 LT_AGE=`expr $BINARY_AGE - $INTERFACE_AGE` LT_CURRENT=`expr $LT_MAJOR + $LT_AGE` LT_REVISION=$INTERFACE_AGE LT_EXTRA="" m4_pattern_allow([LT_MAJOR]) AC_SUBST(LT_RELEASE) AC_SUBST(LT_CURRENT) AC_SUBST(LT_REVISION) AC_SUBST(LT_AGE) AC_SUBST(LT_EXTRA) dnl For use in static assertions AC_DEFINE_UNQUOTED([SDL_BUILD_MAJOR_VERSION], $MAJOR_VERSION, [ ]) AC_DEFINE_UNQUOTED([SDL_BUILD_MINOR_VERSION], $MINOR_VERSION, [ ]) AC_DEFINE_UNQUOTED([SDL_BUILD_MICRO_VERSION], $MICRO_VERSION, [ ]) dnl Detect the canonical build and host environments AC_CANONICAL_HOST dnl Setup for automake AM_INIT_AUTOMAKE([foreign subdir-objects tar-ustar]) dnl Check for tools AC_PROG_AWK AC_PROG_CC AC_PROG_OBJC AC_CHECK_TOOL(RC,[windres],[:]) AC_PROG_INSTALL AC_PROG_FGREP AC_PROG_MAKE_SET PKG_PROG_PKG_CONFIG if [ test -z "$AWK" ]; then AC_MSG_ERROR([*** awk not found, aborting]) fi AC_CHECK_PROGS([SORT], [gsort sort], [false]) AS_IF([! "$SORT" -V /dev/null], [AC_MSG_WARN([sort(1) that supports the -V option is required to find dynamic libraries])]) case "$host" in *-*-beos*) ac_default_prefix=/boot/develop/tools/gnupro ;; *-*-cygwin* | *-*-mingw*) if test "$build" != "$host"; then # cross-compiling # Default cross-compile location ac_default_prefix=/usr/local/cross-tools/$host else # Look for the location of the tools and install there if test "$BUILD_PREFIX" != ""; then ac_default_prefix=$BUILD_PREFIX elif test "$MINGW_PREFIX" != ""; then ac_default_prefix=$MINGW_PREFIX fi fi use_version_rc=true LT_EXTRA="-Wl,src/version.o" # Eliminate libgcc*.dll dependency. CFLAGS="$CFLAGS -static-libgcc" ;; *-*-darwin*) AC_ARG_ENABLE([imageio], [AS_HELP_STRING([--enable-imageio], [use native Mac OS X frameworks for loading images [default=yes]])], [], [enable_imageio=yes]) dnl Show a message when we use ImageIO support so it's not a surprise AC_MSG_CHECKING([for ImageIO support]) AC_MSG_RESULT($enable_imageio) if test x$enable_imageio = xyes; then IMG_LIBS="-Wl,-framework,ApplicationServices -lobjc $IMG_LIBS" CMAKE_LIBS="-Wl,-framework,ApplicationServices;objc;$CMAKE_LIBS" else CFLAGS="$CFLAGS -DSDL_IMAGE_USE_COMMON_BACKEND" fi ;; *-*-os2*) # disable static builds on os/2 enable_static=no # -DBUILD_SDL is needed for DECLSPEC CFLAGS="$CFLAGS -DBUILD_SDL" # OS/2 does not support a DLL name longer than 8 characters. LT_EXTRA="-os2dllname SDL2img" ;; esac AM_CONDITIONAL(USE_IMAGEIO, test x$enable_imageio = xyes) if test x$enable_imageio = xyes; then AC_SUBST([USE_IMAGEIO], 1) else AC_SUBST([USE_IMAGEIO], 0) fi AM_CONDITIONAL(USE_VERSION_RC, test x$use_version_rc = xtrue) dnl set this to use on systems that use lib64 instead of lib base_bindir=`echo \${bindir} | sed 's/.*\/\(.*\)/\1/; q'` base_libdir=`echo \${libdir} | sed 's/.*\/\(.*\)/\1/; q'` dnl Function to find a library in the compiler search path find_lib() { gcc_bin_path=[`$CC -print-search-dirs 2>/dev/null | $FGREP programs: | sed 's/[^=]*=\(.*\)/\1/' | sed 's/:/ /g'`] gcc_lib_path=[`$CC -print-search-dirs 2>/dev/null | $FGREP libraries: | sed 's/[^=]*=\(.*\)/\1/' | sed 's/:/ /g'`] env_lib_path=[`echo $LIBS $LDFLAGS $* | sed 's/-L[ ]*//g'`] env_bin_path=`echo $env_lib_path | sed 's,/lib,/bin,'` if test "$cross_compiling" = yes; then host_lib_path="" else host_lib_path="$ac_default_prefix/$base_libdir $ac_default_prefix/$base_bindir /usr/$base_libdir /usr/local/$base_libdir" fi for path in $env_bin_path $env_lib_path $gcc_bin_path $gcc_lib_path $host_lib_path; do lib=[`ls -- $path/$1 2>/dev/null | sed 's,.*/,,' | $SORT -V -r | $AWK 'BEGIN{FS="."}{ print NF, $0 }' | $SORT -n -s | sed 's,[0-9]* ,,' | head -1`] if test x$lib != x; then echo $lib return fi done } CheckNoUndef() { AC_MSG_CHECKING(for linker option --no-undefined) have_no_undefined=no case "${host_os}" in dnl Skip this on platforms where it is just simply busted. openbsd*) ;; darwin*) have_no_undefined="-Wl,-undefined,error" LDFLAGS="$LDFLAGS -Wl,-undefined,error" ;; *) save_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS -Wl,--no-undefined" AC_LINK_IFELSE([AC_LANG_PROGRAM], [have_no_undefined=yes],[LDFLAGS="$save_LDFLAGS"]) ;; esac AC_MSG_RESULT($have_no_undefined) } dnl See if GCC's -Wall is supported. CheckWarnAll() { AC_MSG_CHECKING(for GCC -Wall option) have_gcc_Wall=no save_CFLAGS="$CFLAGS" CFLAGS="$save_CFLAGS -Wall" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([int x = 0;])], [have_gcc_Wall=yes]) AC_MSG_RESULT($have_gcc_Wall) CFLAGS="$save_CFLAGS" if test x$have_gcc_Wall = xyes; then CFLAGS="$CFLAGS -Wall" dnl Haiku headers use multicharacter constants all over the place. Ignore these warnings when using -Wall. AC_MSG_CHECKING(for necessary GCC -Wno-multichar option) need_gcc_Wno_multichar=no case "$host" in *-*-haiku*) need_gcc_Wno_multichar=yes ;; esac AC_MSG_RESULT($need_gcc_Wno_multichar) if test x$need_gcc_Wno_multichar = xyes; then CFLAGS="$CFLAGS -Wno-multichar" fi fi } dnl See if GCC's -fvisibility=hidden is supported (gcc4 and later, usually). CheckVisibilityHidden() { AC_MSG_CHECKING(for GCC -fvisibility=hidden option) have_gcc_fvisibility=no case "$host" in *-*-cygwin* | *-*-mingw* | *-*-os2*) AC_MSG_RESULT([ignored for $host_os]) return ;; esac visibility_CFLAGS="-fvisibility=hidden" save_CFLAGS="$CFLAGS" CFLAGS="$save_CFLAGS $visibility_CFLAGS -Werror" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if !defined(__GNUC__) || __GNUC__ < 4 #error SDL only uses visibility attributes in GCC 4 or newer #endif ]],[])], [have_gcc_fvisibility=yes],[]) AC_MSG_RESULT($have_gcc_fvisibility) CFLAGS="$save_CFLAGS" if test x$have_gcc_fvisibility = xyes; then CFLAGS="$CFLAGS $visibility_CFLAGS" fi } dnl Check command-line options AC_ARG_ENABLE([stb_image], [AS_HELP_STRING([--enable-stb-image], [use stb_image for loading JPG and PNG images [default=yes]])], [], [enable_stb_image=yes]) AC_ARG_ENABLE([avif], [AS_HELP_STRING([--enable-avif], [support loading AVIF images [default=yes]])], [], [enable_avif=yes]) AC_ARG_ENABLE([avif-shared], [AS_HELP_STRING([--enable-avif-shared@<:@=SONAME@:>@], [dynamically load AVIF support [default=yes]])], [], [enable_avif_shared=yes]) AC_ARG_ENABLE([bmp], [AS_HELP_STRING([--enable-bmp], [support loading BMP/ICO/CUR images [default=yes]])], [], [enable_bmp=yes]) AC_ARG_ENABLE([gif], [AS_HELP_STRING([--enable-gif], [support loading GIF images [default=yes]])], [], [enable_gif=yes]) AC_ARG_ENABLE([jpg], [AS_HELP_STRING([--enable-jpg], [support loading JPG images [default=yes]])], [], [enable_jpg=yes]) AC_ARG_ENABLE([jpg-shared], [AS_HELP_STRING([--enable-jpg-shared@<:@=SONAME@:>@], [dynamically load JPG support [default=yes]])], [], [enable_jpg_shared=yes]) AC_ARG_ENABLE([save-jpg], [AS_HELP_STRING([--enable-save-jpg], [support saving JPG images [default=yes]])], [], [enable_save_jpg=yes]) AC_ARG_ENABLE([jxl], [AS_HELP_STRING([--enable-jxl], [support loading JXL images [default=yes]])], [], [enable_jxl=yes]) AC_ARG_ENABLE([jxl-shared], [AS_HELP_STRING([--enable-jxl-shared@<:@=SONAME@:>@], [dynamically load JXL support [default=yes]])], [], [enable_jxl_shared=yes]) AC_ARG_ENABLE([lbm], [AS_HELP_STRING([--enable-lbm], [support loading LBM images [default=yes]])], [], [enable_lbm=yes]) AC_ARG_ENABLE([pcx], [AS_HELP_STRING([--enable-pcx], [support loading PCX images [default=yes]])], [], [enable_pcx=yes]) AC_ARG_ENABLE([png], [AS_HELP_STRING([--enable-png], [support loading PNG images [default=yes]])], [], [enable_png=yes]) AC_ARG_ENABLE([png-shared], [AS_HELP_STRING([--enable-png-shared@<:@=SONAME@:>@], [dynamically load PNG support [default=yes]])], [], [enable_png_shared=yes]) AC_ARG_ENABLE([save-png], [AS_HELP_STRING([--enable-save-png], [support saving PNG images [default=yes]])], [], [enable_save_png=yes]) AC_ARG_ENABLE([pnm], [AS_HELP_STRING([--enable-pnm], [support loading PNM images [default=yes]])], [], [enable_pnm=yes]) AC_ARG_ENABLE([svg], [AS_HELP_STRING([--enable-svg], [support loading SVG images [default=yes]])], [], [enable_svg=yes]) AC_ARG_ENABLE([tga], [AS_HELP_STRING([--enable-tga], [support loading TGA images [default=yes]])], [], [enable_tga=yes]) AC_ARG_ENABLE([tif], [AS_HELP_STRING([--enable-tif], [support loading TIFF images [default=yes]])], [], [enable_tif=yes]) AC_ARG_ENABLE([tif-shared], [AS_HELP_STRING([--enable-tif-shared@<:@=SONAME@:>@], [dynamically load TIFF support [default=yes]])], [], [enable_tif_shared=yes]) AC_ARG_ENABLE([xcf], [AS_HELP_STRING([--enable-xcf], [support loading XCF images [default=yes]])], [], [enable_xcf=yes]) AC_ARG_ENABLE([xpm], [AS_HELP_STRING([--enable-xpm], [support loading XPM images [default=yes]])], [], [enable_xpm=yes]) AC_ARG_ENABLE([xv], [AS_HELP_STRING([--enable-xv], [support loading XV images [default=yes]])], [], [enable_xv=yes]) AC_ARG_ENABLE([webp], [AS_HELP_STRING([--enable-webp], [support loading WEBP images [default=yes]])], [], [enable_webp=yes]) AC_ARG_ENABLE([webp-shared], [AS_HELP_STRING([--enable-webp-shared], [dynamically load WEBP support [default=yes]])], [], [enable_webp_shared=yes]) AC_ARG_ENABLE([qoi], [AS_HELP_STRING([--enable-qoi], [support loading QOI images [default=yes]])], [], [enable_qoi=yes]) AC_ARG_ENABLE([tests], [AS_HELP_STRING([--enable-tests], [build tests [default=no]])], [], [enable_tests=no]) AC_ARG_ENABLE([installed-tests], [AS_HELP_STRING([--enable-installed-tests], [install tests [default=no]])], [], [enable_installed_tests=no]) dnl Check for SDL SDL_VERSION=2.0.9 AC_SUBST(SDL_VERSION) AM_PATH_SDL2($SDL_VERSION, :, AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!]) ) CFLAGS="$CFLAGS $SDL_CFLAGS" LIBS="$LIBS $SDL_LIBS" if test x$enable_stb_image = xyes; then AC_DEFINE([USE_STBIMAGE]) AC_SUBST([USE_STBIMAGE], 1) else AC_SUBST([USE_STBIMAGE], 0) fi AC_SUBST([USE_WIC], 0) load_avif=0 if test x$enable_avif = xyes; then PKG_CHECK_MODULES([LIBAVIF], [libavif >= 0.9.3], [dnl have_avif_hdr=yes have_avif_lib=yes have_avif_pc=yes ], [dnl save_LIBS="$LIBS" LIBS="-lavif" AC_LANG_PUSH([C]) AC_MSG_CHECKING([for libavif >= 0.9.3]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ #if (AVIF_VERSION < 90300) #error libavif too old. #endif return !!avifVersion(); ]])],[ have_avif_hdr=yes have_avif_lib=yes LIBAVIF_CFLAGS="" LIBAVIF_LIBS="-lavif" ], [have_avif_lib=no]) LIBS="$save_LIBS" AC_LANG_POP([C]) AC_MSG_RESULT([$have_avif_lib]) ]) if test x$have_avif_hdr = xyes -a x$have_avif_lib = xyes; then if test x$enable_avif = xyes; then AC_DEFINE([LOAD_AVIF]) fi case "$host" in *-*-darwin*) avif_lib=[`find_lib "libavif.[0-9]*.dylib"`] if test x$avif_lib = x; then avif_lib=[`find_lib "libavif*.dylib"`] fi ;; *-*-cygwin* | *-*-mingw*) avif_lib=[`find_lib "libavif-[0-9]*.dll"`] if test x$avif_lib = x; then avif_lib=[`find_lib "libavif*.dll"`] fi ;; *) avif_lib=[`find_lib "libavif[0-9]*.so.*"`] if test x$avif_lib = x; then avif_lib=[`find_lib "libavif.so.*"`] fi ;; esac load_avif=1 AS_CASE(["$enable_avif_shared"], [yes | no], [], [*], [avif_lib="$enable_avif_shared"]) elif test x$enable_avif = xyes; then AC_MSG_WARN([*** Unable to find AVIF library (https://github.com/AOMediaCodec/libavif)]) AC_MSG_WARN([AVIF image loading disabled]) fi fi AC_SUBST([LOAD_AVIF], $load_avif) load_jpg=0 if test x$enable_jpg = xyes; then if test x$enable_stb_image = xyes; then AC_DEFINE([LOAD_JPG]) load_jpg=1 elif test x$enable_imageio = xyes; then AC_DEFINE([LOAD_JPG]) load_jpg=1 AC_DEFINE([JPG_USES_IMAGEIO]) else PKG_CHECK_MODULES([LIBJPEG], [libjpeg], [dnl have_jpg_hdr=yes have_jpg_lib=yes have_jpg_pc=yes ], [dnl AC_CHECK_HEADER([jpeglib.h], [ have_jpg_hdr=yes LIBJPEG_CFLAGS="" ]) AC_CHECK_LIB([jpeg], [jpeg_CreateDecompress], [ have_jpg_lib=yes LIBJPEG_LIBS="-ljpeg" ]) ]) if test x$have_jpg_hdr = xyes -a x$have_jpg_lib = xyes; then if test x$enable_jpg = xyes; then AC_DEFINE([LOAD_JPG]) load_jpg=1 fi case "$host" in *-*-darwin*) jpg_lib=[`find_lib "libjpeg.[0-9]*.dylib"`] if test x$jpg_lib = x; then jpg_lib=[`find_lib libjpeg.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) jpg_lib=[`find_lib "libjpeg*.dll"`] ;; *) jpg_lib=[`find_lib "libjpeg[0-9]*.so.*"`] if test x$jpg_lib = x; then jpg_lib=[`find_lib "libjpeg.so.*"`] fi ;; esac AS_CASE(["$enable_jpg_shared"], [yes | no], [], [*], [jpg_lib="$enable_jpg_shared"]) else AC_MSG_WARN([*** Unable to find JPEG library (http://www.ijg.org/)]) AC_MSG_WARN([JPG image loading disabled]) fi fi fi AC_SUBST([LOAD_JPG], $load_jpg) load_jxl=0 if test x$enable_jxl = xyes; then PKG_CHECK_MODULES([LIBJXL], [libjxl], [dnl have_jxl_hdr=yes have_jxl_lib=yes have_jxl_pc=yes ], [dnl AC_CHECK_HEADER([jxl/decode.h], [ have_jxl_hdr=yes LIBJXL_CFLAGS="" ]) AC_CHECK_LIB([jxl], [JxlSignatureCheck], [ have_jxl_lib=yes LIBJXL_LIBS="-ljxl" ]) ]) if test x$have_jxl_hdr = xyes -a x$have_jxl_lib = xyes; then if test x$enable_jxl = xyes; then AC_DEFINE([LOAD_JXL]) load_jxl=1 fi case "$host" in *-*-darwin*) jxl_lib=[`find_lib "libjxl.[0-9]*.dylib"`] if test x$jxl_lib = x; then jxl_lib=[`find_lib libjxl.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) jxl_lib=[`find_lib "libjxl-[0-9]*.dll"`] if test x$jxl_lib = x; then jxl_lib=[`find_lib "libjxl.dll"`] fi ;; *) jxl_lib=[`find_lib "libjxl[0-9]*.so.*"`] if test x$jxl_lib = x; then jxl_lib=[`find_lib "libjxl.so.*"`] fi ;; esac AS_CASE(["$enable_jxl_shared"], [yes | no], [], [*], [jxl_lib="$enable_jxl_shared"]) elif test x$enable_jxl = xyes; then AC_MSG_WARN([*** Unable to find JXL library (https://jpegxl.info/)]) AC_MSG_WARN([JXL image loading disabled]) fi fi AC_SUBST([LOAD_JXL], $load_jxl) load_png=0 if test x$enable_png = xyes; then if test x$enable_stb_image = xyes; then AC_DEFINE([LOAD_PNG]) load_png=1 elif test x$enable_imageio = xyes; then AC_DEFINE([LOAD_PNG]) AC_DEFINE([PNG_USES_IMAGEIO]) load_png=1 else PKG_CHECK_MODULES([LIBPNG], [libpng], [dnl have_png_hdr=yes have_png_lib=yes have_png_pc=yes ], [dnl AC_CHECK_HEADER([png.h], [ have_png_hdr=yes LIBPNG_CFLAGS="" ]) AC_CHECK_LIB([png], [png_create_read_struct], [ have_png_lib=yes LIBPNG_LIBS="-lpng -lz" ], [], [-lz]) ]) if test x$have_png_hdr = xyes -a x$have_png_lib = xyes; then AC_DEFINE([LOAD_PNG]) load_png=1 case "$host" in *-*-darwin*) png_lib=[`find_lib "libpng[0-9]*.dylib"`] if test x$png_lib = x; then png_lib=[`find_lib libpng.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) png_lib=[`find_lib "libpng*.dll"`] ;; *) png_lib=[`find_lib "libpng[0-9]*.so.*"`] if test x$png_lib = x; then png_lib=[`find_lib "libpng.so.*"`] fi ;; esac AS_CASE(["$enable_png_shared"], [yes | no], [], [*], [png_lib="$enable_png_shared"]) else AC_MSG_WARN([*** Unable to find PNG library (http://www.libpng.org/pub/png/)]) AC_MSG_WARN([PNG image loading disabled]) fi fi fi AC_SUBST([LOAD_PNG], $load_png) load_tif=0 if test x$enable_tif = xyes -a x$enable_imageio != xyes; then PKG_CHECK_MODULES([LIBTIFF], [libtiff-4], [dnl have_tif_hdr=yes have_tif_lib=yes have_tif_pc=yes ], [dnl AC_CHECK_HEADER([tiffio.h], [ have_tif_hdr=yes LIBTIFF_CFLAGS="" ]) AC_CHECK_LIB([tiff], [TIFFClientOpen], [ have_tif_lib=yes LIBTIFF_LIBS="-ltiff -lz" ], [], [-lz]) ]) if test x$have_tif_hdr = xyes -a x$have_tif_lib = xyes; then AC_DEFINE([LOAD_TIF]) load_tif=1 case "$host" in *-*-darwin*) tif_lib=[`find_lib "libtiff.[0-9]*.dylib"`] if test x$tif_lib = x; then tif_lib=[`find_lib libtiff.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) tif_lib=[`find_lib "libtiff-*.dll"`] ;; *) tif_lib=[`find_lib "libtiff[0-9]*.so.*"`] if test x$tif_lib = x; then tif_lib=[`find_lib "libtiff.so.*"`] fi ;; esac AS_CASE(["$enable_tif_shared"], [yes | no], [], [*], [tif_lib="$enable_tif_shared"]) else AC_MSG_WARN([*** Unable to find Tiff library (http://www.simplesystems.org/libtiff/)]) AC_MSG_WARN([TIF image loading disabled]) fi fi AC_SUBST([LOAD_TIF], $load_tif) if test x$enable_webp = xyes; then PKG_CHECK_MODULES([LIBWEBPDEMUX], [libwebpdemux], [dnl have_webpdemux_hdr=yes have_webpdemux_lib=yes have_webpdemux_pc=yes ], [dnl AC_CHECK_HEADER([webp/demux.h], [ have_webpdemux_hdr=yes LIBWEBPDEMUX_CFLAGS="" ]) AC_CHECK_LIB([webpdemux], [WebPDemuxGetFrame], [ have_webpdemux_lib=yes LIBWEBPDEMUX_LIBS="-lwebpdemux" ], [], [-lm]) ]) PKG_CHECK_MODULES([LIBWEBP], [libwebp], [dnl have_webp_hdr=yes have_webp_lib=yes have_webp_pc=yes ], [dnl AC_CHECK_HEADER([webp/decode.h], [ have_webp_hdr=yes LIBWEBP_CFLAGS="" ]) AC_CHECK_LIB([webp], [WebPGetDecoderVersion], [ have_webp_lib=yes LIBWEBP_LIBS="-lwebp" ], [], [-lm]) ]) if test x$have_webpdemux_hdr = xyes -a x$have_webpdemux_lib = xyes -a x$have_webp_hdr = xyes -a x$have_webp_lib = xyes; then AC_DEFINE([LOAD_WEBP]) load_webp=1 case "$host" in *-*-darwin*) webpdemux_lib=[`find_lib "libwebpdemux.[0-9]*.dylib"`] webp_lib=[`find_lib "libwebp.[0-9]*.dylib"`] if test x$webp_lib = x; then webpdemux_lib=[`find_lib libwebpdemux.dylib`] webp_lib=[`find_lib libwebp.dylib`] fi ;; *-*-cygwin* | *-*-mingw*) webpdemux_lib=[`find_lib "libwebpdemux-*.dll"`] webp_lib=[`find_lib "libwebp-*.dll"`] ;; *) webpdemux_lib=[`find_lib "libwebpdemux[0-9]*.so.*"`] webp_lib=[`find_lib "libwebp[0-9]*.so.*"`] if test x$webp_lib = x; then webpdemux_lib=[`find_lib "libwebpdemux.so.*"`] webp_lib=[`find_lib "libwebp.so.*"`] fi ;; esac else AC_MSG_WARN([*** Unable to find WEBP library (https://developers.google.com/speed/webp)]) AC_MSG_WARN([WEBP image loading disabled]) fi fi AC_SUBST([LOAD_WEBP], $load_webp) load_bmp=0 if test x$enable_bmp = xyes; then AC_DEFINE([LOAD_BMP]) load_bmp=1 fi AC_SUBST([LOAD_BMP], $load_bmp) load_gif=0 if test x$enable_gif = xyes; then AC_DEFINE([LOAD_GIF]) load_gif=1 fi AC_SUBST([LOAD_GIF], $load_gif) load_lbm=0 if test x$enable_lbm = xyes; then AC_DEFINE([LOAD_LBM]) load_lbm=1 fi AC_SUBST([LOAD_LBM], $load_lbm) load_pcx=0 if test x$enable_pcx = xyes; then AC_DEFINE([LOAD_PCX]) load_pcx=1 fi AC_SUBST([LOAD_PCX], $load_pcx) load_pnm=0 if test x$enable_pnm = xyes; then AC_DEFINE([LOAD_PNM]) load_pnm=1 fi AC_SUBST([LOAD_PNM], $load_pnm) load_svg=0 if test x$enable_svg = xyes; then AC_DEFINE([LOAD_SVG]) load_svg=1 fi AC_SUBST([LOAD_SVG], $load_svg) load_tga=0 if test x$enable_tga = xyes; then AC_DEFINE([LOAD_TGA]) load_tga=1 fi AC_SUBST([LOAD_TGA], $load_tga) load_xcf=0 if test x$enable_xcf = xyes; then AC_DEFINE([LOAD_XCF]) load_xcf=1 fi AC_SUBST([LOAD_XCF], $load_xcf) load_xpm=0 if test x$enable_xpm = xyes; then AC_DEFINE([LOAD_XPM]) load_xpm=1 fi AC_SUBST([LOAD_XPM], $load_xpm) load_xv=0 if test x$enable_xv = xyes; then AC_DEFINE([LOAD_XV]) load_xv=1 fi AC_SUBST([LOAD_XV], $load_xv) load_qoi=0 if test x$enable_qoi = xyes; then AC_DEFINE([LOAD_QOI]) load_qoi=1 fi AC_SUBST([LOAD_QOI], $load_qoi) if test x$enable_webp = xyes -a x$have_webp_hdr = xyes -a x$have_webp_lib -a x$have_webpdemux_hdr = xyes -a x$have_webpdemux_lib = xyes; then CFLAGS="$LIBWEBP_CFLAGS $CFLAGS" if test x$enable_webp_shared != xno && test x$webp_lib != x; then echo "-- dynamic libwebp -> $webp_lib" AC_DEFINE_UNQUOTED(LOAD_WEBP_DYNAMIC, "$webp_lib") echo "-- dynamic libwebpdemux -> $webpdemux_lib" AC_DEFINE_UNQUOTED(LOAD_WEBPDEMUX_DYNAMIC, "$webpdemux_lib") else IMG_LIBS="$LIBWEBPDEMUX_LIBS $LIBWEBP_LIBS $IMG_LIBS" CMAKE_LIBS="WebP::webp;WebP::webpdemux;$CMAKE_LIBS" if test x$have_webp_pc = xyes; then PC_REQUIRES="libwebpdemux libwebp $PC_REQUIRES" else PC_LIBS="$LIBWEBPDEMUX_LIBS $LIBWEBP_LIBS $PC_LIBS" fi fi fi if test x$enable_avif = xyes -a x$have_avif_hdr = xyes -a x$have_avif_lib = xyes; then CFLAGS="$LIBAVIF_CFLAGS $CFLAGS" if test x$enable_avif_shared != xno && test x$avif_lib != x; then echo "-- dynamic libavif -> $avif_lib" AC_DEFINE_UNQUOTED(LOAD_AVIF_DYNAMIC, "$avif_lib") else IMG_LIBS="$LIBAVIF_LIBS $IMG_LIBS" CMAKE_LIBS="avif;$CMAKE_LIBS" if test x$have_avif_pc = xyes; then PC_REQUIRES="libavif $PC_REQUIRES" else PC_LIBS="$LIBAVIF_LIBS $PC_LIBS" fi fi fi if test x$enable_tif = xyes -a x$have_tif_hdr = xyes -a x$have_tif_lib = xyes; then CFLAGS="$LIBTIFF_CFLAGS $CFLAGS" if test x$enable_tif_shared != xno && test x$tif_lib != x; then echo "-- dynamic libtiff -> $tif_lib" AC_DEFINE_UNQUOTED(LOAD_TIF_DYNAMIC, "$tif_lib") else if test x$have_libjpeg = xyes; then # Disable dynamic jpeg since we're linking it explicitly jpg_lib='' fi IMG_LIBS="$LIBTIFF_LIBS $IMG_LIBS" CMAKE_LIBS="TIFF::TIFF;$CMAKE_LIBS" if test x$have_tif_pc = xyes; then PC_REQUIRES="libtiff-4 $PC_REQUIRES" else PC_LIBS="$LIBTIFF_LIBS $PC_LIBS" fi fi fi if test x$enable_jpg = xyes -a x$have_jpg_hdr = xyes -a x$have_jpg_lib = xyes; then CFLAGS="$LIBJPEG_CFLAGS $CFLAGS" if test x$enable_jpg_shared != xno && test x$jpg_lib != x; then echo "-- dynamic libjpeg -> $jpg_lib" AC_DEFINE_UNQUOTED(LOAD_JPG_DYNAMIC, "$jpg_lib") else IMG_LIBS="$LIBJPEG_LIBS $IMG_LIBS" CMAKE_LIBS="JPEG::JPEG;$CMAKE_LIBS" if test x$have_jpg_pc = xyes; then PC_REQUIRES="libjpeg $PC_REQUIRES" else PC_LIBS="$LIBJPEG_LIBS $PC_LIBS" fi fi fi if test x$enable_jxl = xyes -a x$have_jxl_hdr = xyes -a x$have_jxl_lib = xyes; then CFLAGS="$LIBJXL_CFLAGS $CFLAGS" if test x$enable_jxl_shared != xno && test x$jxl_lib != x; then echo "-- dynamic libjxl -> $jxl_lib" AC_DEFINE_UNQUOTED(LOAD_JXL_DYNAMIC, "$jxl_lib") else IMG_LIBS="$LIBJXL_LIBS $IMG_LIBS" CMAKE_LIBS="libjxl::libjxl;$CMAKE_LIBS" if test x$have_jxl_pc = xyes; then PC_REQUIRES="libjxl $PC_REQUIRES" else PC_LIBS="$LIBJXL_LIBS $PC_LIBS" fi fi fi if test x$enable_png = xyes -a x$have_png_hdr = xyes -a x$have_png_lib = xyes; then CFLAGS="$LIBPNG_CFLAGS $CFLAGS" if test x$enable_png_shared != xno && test x$png_lib != x; then echo "-- dynamic libpng -> $png_lib" AC_DEFINE_UNQUOTED(LOAD_PNG_DYNAMIC, "$png_lib") else IMG_LIBS="$LIBPNG_LIBS $IMG_LIBS" CMAKE_LIBS="PNG::PNG;$CMAKE_LIBS" if test x$have_png_pc = xyes; then PC_REQUIRES="libpng $PC_REQUIRES" else PC_LIBS="$LIBPNG_LIBS $PC_LIBS" fi fi fi if test x$enable_save_png = xyes; then AC_DEFINE([SDL_IMAGE_SAVE_PNG], 1) AC_SUBST([SDL2IMAGE_PNG_SAVE], 1) else AC_DEFINE([SDL_IMAGE_SAVE_PNG], 0) AC_SUBST([SDL2IMAGE_PNG_SAVE], 0) fi if test x$enable_save_jpg = xyes; then AC_DEFINE([SDL_IMAGE_SAVE_JPG], 1) AC_SUBST([SDL2IMAGE_JPG_SAVE], 1) else AC_DEFINE([SDL_IMAGE_SAVE_JPG], 0) AC_SUBST([SDL2IMAGE_JPG_SAVE], 0) fi AC_SUBST([IMG_LIBS]) AC_SUBST([CMAKE_LIBS]) AC_SUBST([PC_LIBS]) AC_SUBST([PC_REQUIRES]) AM_CONDITIONAL([BUILD_TESTS], [test "x$enable_tests" = xyes]) AM_CONDITIONAL([INSTALL_TESTS], [test "x$enable_installed_tests" = xyes]) dnl Calculate the location of the prefix, relative to the cmake folder pkg_cmakedir='$libdir/cmake/SDL2_image' AX_COMPUTE_RELATIVE_PATHS([pkg_cmakedir:prefix:cmake_prefix_relpath]) AC_SUBST([cmake_prefix_relpath]) dnl check for LD --no-undefined option CheckNoUndef dnl check for GCC warning options CheckWarnAll dnl check for GCC visibility attributes CheckVisibilityHidden OBJCFLAGS=$CFLAGS # Finally create all the generated files AC_CONFIG_FILES([ Makefile sdl2_image-config.cmake sdl2_image-config-version.cmake SDL2_image.spec SDL2_image.pc test/Makefile ]) AC_OUTPUT SDL2_image-2.8.8/docs/README-emscripten.txt0000664000000000000000000000142214444663624015140 0ustar00Building SDL2_image ------------------- The easiest way to use SDL2_image with Emscripten is to use Emscripten ports (https://kripken.github.io/emscripten-site/docs/compiling/Building-Projects.html#emscripten-ports) (-s USE_SDL_IMAGE=2). If you want to build it yourself instead you can use these instructions: Step 0 - get emscripten Step 1 - get sdl2-emscripten * clone https://github.com/emscripten-ports/SDL2.git * follow the build instructions in SDL2/docs/README-emscripten.md (make sure to pass a --prefix to configure) * make install Step 2 - get sdl_image * emconfigure ./configure --disable-sdltest --with-sdl-prefix=/path/to/sdl --prefix=/path/to/install * (where /path/to/sdl is the path you passed as --prefix to SDL2 configure) * emmake make * make install SDL2_image-2.8.8/docs/README-versions.md0000664000000000000000000000501014444663624014415 0ustar00# Versioning ## Since 2.5.0 `SDL_image` follows an "odd/even" versioning policy, similar to GLib, GTK, Flatpak and older versions of the Linux kernel: * The major version (first part) increases when backwards compatibility is broken, which will happen infrequently. * If the minor version (second part) is divisible by 2 (for example 2.6.x, 2.8.x), this indicates a version that is believed to be stable and suitable for production use. * In stable releases, the patchlevel or micro version (third part) indicates bugfix releases. Bugfix releases should not add or remove ABI, so the ".0" release (for example 2.6.0) should be forwards-compatible with all the bugfix releases from the same cycle (for example 2.6.1). * The minor version increases when new API or ABI is added, or when other significant changes are made. Newer minor versions are backwards-compatible, but not fully forwards-compatible. For example, programs built against `SDL_image` 2.6.x should work fine with 2.8.x, but programs built against 2.8.x will not necessarily work with 2.6.x. * If the minor version (second part) is not divisible by 2 (for example 2.5.x, 2.7.x), this indicates a development prerelease that is not suitable for stable software distributions. Use with caution. * The patchlevel or micro version (third part) increases with each prerelease. * Each prerelease might add new API and/or ABI. * Prereleases are backwards-compatible with older stable branches. For example, 2.7.x will be backwards-compatible with 2.6.x. * Prereleases are not guaranteed to be backwards-compatible with each other. For example, new API or ABI added in 2.5.1 might be removed or changed in 2.5.2. If this would be a problem for you, please do not use prereleases. * Only upgrade to a prerelease if you can guarantee that you will promptly upgrade to the stable release that follows it. For example, do not upgrade to 2.5.x unless you will be able to upgrade to 2.6.0 when it becomes available. * Software distributions that have a freeze policy (in particular Linux distributions with a release cycle, such as Debian and Fedora) should usually only package stable releases, and not prereleases. ## Before 2.5.0 Older versions of `SDL_image` used the patchlevel (micro version, third part) for feature releases, and did not distinguish between feature and bugfix releases. SDL2_image-2.8.8/docs/release_checklist.md0000664000000000000000000000556614444663624015303 0ustar00# Release checklist ## New feature release * Update `CHANGES.txt` * Bump version number to 2.EVEN.0 in all these locations: * `SDL_image.h`: `SDL_IMAGE_MAJOR_VERSION`, `SDL_IMAGE_MINOR_VERSION`, `SDL_IMAGE_PATCHLEVEL` * `configure.ac`: `MAJOR_VERSION_MACRO`, `MINOR_VERSION_MACRO`, `MICRO_VERSION_MACRO` * `CMakeLists.txt`: `MAJOR_VERSION`, `MINOR_VERSION`, `MICRO_VERSION` * `Makefile.os2`: `MAJOR_VERSION`, `MINOR_VERSION`, `MICRO_VERSION` * `src/version.rc`: `FILEVERSION`, `PRODUCTVERSION`, `FileVersion`, `ProductVersion` * `VisualC/Version.rc`: `FILEVERSION`, `PRODUCTVERSION`, `FileVersion`, `ProductVersion` * `Xcode/Info-Framework.plist`: `CFBundleShortVersionString`, `CFBundleVersion` * Bump ABI version information * `Xcode/SDL_image.xcodeproj/project.pbxproj`: `DYLIB_CURRENT_VERSION`, `DYLIB_COMPATIBILITY_VERSION` * set first number in `DYLIB_CURRENT_VERSION` to (100 * *minor*) + 1 * set second number in `DYLIB_CURRENT_VERSION` to 0 * set `DYLIB_COMPATIBILITY_VERSION` to the same value * Regenerate `configure` * Run `./build-scripts/test-versioning.sh` to verify that everything is consistent * Do the release ## New bugfix release * Check that no new API/ABI was added * If it was, do a new feature release (see above) instead * Bump version number from 2.Y.Z to 2.Y.(Z+1) (Y is even) * Same places as listed above * Bump ABI version information * `Xcode/SDL_image.xcodeproj/project.pbxproj`: `DYLIB_CURRENT_VERSION`, `DYLIB_COMPATIBILITY_VERSION` * set second number in `DYLIB_CURRENT_VERSION` to *patchlevel* * Leave `DYLIB_COMPATIBILITY_VERSION` unchanged * Regenerate `configure` * Run `./build-scripts/test-versioning.sh` to verify that everything is consistent * Do the release ## After a feature release * Create a branch like `release-2.6.x` * Bump version number to 2.ODD.0 for next development branch * Same places as listed above * Bump ABI version information * Same places as listed above * Assume that the next feature release will contain new API/ABI * Run `./build-scripts/test-versioning.sh` to verify that everything is consistent * Add a new milestone for issues ## New development prerelease * Bump version number from 2.Y.Z to 2.Y.(Z+1) (Y is odd) * Same places as listed above * Bump ABI version information * `Xcode/SDL_image.xcodeproj/project.pbxproj`: `DYLIB_CURRENT_VERSION`, `DYLIB_COMPATIBILITY_VERSION` * set first number in `DYLIB_CURRENT_VERSION` to (100 * *minor*) + *patchlevel* + 1 * set second number in `DYLIB_CURRENT_VERSION` to 0 * set `DYLIB_COMPATIBILITY_VERSION` to the same value * Regenerate `configure` * Run `./build-scripts/test-versioning.sh` to verify that everything is consistent * Do the release SDL2_image-2.8.8/examples/showanim.c0000664000000000000000000001367314751445211014154 0ustar00/* showanim: A test application for the SDL image loading library. Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "SDL.h" #include "SDL_image.h" /* Draw a Gimpish background pattern to show transparency in the image */ static void draw_background(SDL_Renderer *renderer, int w, int h) { SDL_Color col[2] = { { 0x66, 0x66, 0x66, 0xff }, { 0x99, 0x99, 0x99, 0xff }, }; int i, x, y; SDL_Rect rect; rect.w = 8; rect.h = 8; for (y = 0; y < h; y += rect.h) { for (x = 0; x < w; x += rect.w) { /* use an 8x8 checkerboard pattern */ i = (((x ^ y) >> 3) & 1); SDL_SetRenderDrawColor(renderer, col[i].r, col[i].g, col[i].b, col[i].a); rect.x = x; rect.y = y; SDL_RenderFillRect(renderer, &rect); } } } int main(int argc, char *argv[]) { SDL_Window *window; SDL_Renderer *renderer; IMG_Animation *anim; SDL_Texture **textures; Uint32 flags; int i, j, w, h, done; int once = 0; int current_frame, delay; SDL_Event event; /* Check command line usage */ if ( ! argv[1] ) { SDL_Log("Usage: %s [-fullscreen] ...\n", argv[0]); return(1); } flags = SDL_WINDOW_HIDDEN; for ( i=1; argv[i]; ++i ) { if ( SDL_strcmp(argv[i], "-fullscreen") == 0 ) { SDL_ShowCursor(0); flags |= SDL_WINDOW_FULLSCREEN; } } if (SDL_Init(SDL_INIT_VIDEO) == -1) { SDL_Log("SDL_Init(SDL_INIT_VIDEO) failed: %s\n", SDL_GetError()); return(2); } if (SDL_CreateWindowAndRenderer(0, 0, flags, &window, &renderer) < 0) { SDL_Log("SDL_CreateWindowAndRenderer() failed: %s\n", SDL_GetError()); return(2); } for ( i=1; argv[i]; ++i ) { if ( SDL_strcmp(argv[i], "-fullscreen") == 0 ) { continue; } if ( SDL_strcmp(argv[i], "-once") == 0 ) { once = 1; continue; } /* Open the image file */ anim = IMG_LoadAnimation(argv[i]); if (!anim) { SDL_Log("Couldn't load %s: %s\n", argv[i], SDL_GetError()); continue; } w = anim->w; h = anim->h; textures = (SDL_Texture **)SDL_calloc(anim->count, sizeof(*textures)); if (!textures) { SDL_Log("Couldn't allocate textures\n"); IMG_FreeAnimation(anim); continue; } for (j = 0; j < anim->count; ++j) { textures[j] = SDL_CreateTextureFromSurface(renderer, anim->frames[j]); } current_frame = 0; /* Show the window */ SDL_SetWindowTitle(window, argv[i]); SDL_SetWindowSize(window, w, h); SDL_ShowWindow(window); done = 0; while ( ! done ) { while ( SDL_PollEvent(&event) ) { switch (event.type) { case SDL_KEYUP: switch (event.key.keysym.sym) { case SDLK_LEFT: if ( i > 1 ) { i -= 2; done = 1; } break; case SDLK_RIGHT: if ( argv[i+1] ) { done = 1; } break; case SDLK_ESCAPE: case SDLK_q: argv[i+1] = NULL; /* Drop through to done */ case SDLK_SPACE: case SDLK_TAB: done = 1; break; default: break; } break; case SDL_MOUSEBUTTONDOWN: done = 1; break; case SDL_QUIT: argv[i+1] = NULL; done = 1; break; default: break; } } /* Draw a background pattern in case the image has transparency */ draw_background(renderer, w, h); /* Display the image */ SDL_RenderCopy(renderer, textures[current_frame], NULL, NULL); SDL_RenderPresent(renderer); if (anim->delays[current_frame]) { delay = anim->delays[current_frame]; } else { delay = 100; } SDL_Delay(delay); current_frame = (current_frame + 1) % anim->count; if (once && current_frame == 0) { break; } } for (j = 0; j < anim->count; ++j) { SDL_DestroyTexture(textures[j]); } IMG_FreeAnimation(anim); } SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); /* We're done! */ SDL_Quit(); return(0); } SDL2_image-2.8.8/examples/showimage.c0000664000000000000000000001413014751445211014277 0ustar00/* showimage: A test application for the SDL image loading library. Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "SDL.h" #include "SDL_image.h" /* Draw a Gimpish background pattern to show transparency in the image */ static void draw_background(SDL_Renderer *renderer, int w, int h) { SDL_Color col[2] = { { 0x66, 0x66, 0x66, 0xff }, { 0x99, 0x99, 0x99, 0xff }, }; int i, x, y; SDL_Rect rect; rect.w = 8; rect.h = 8; for (y = 0; y < h; y += rect.h) { for (x = 0; x < w; x += rect.w) { /* use an 8x8 checkerboard pattern */ i = (((x ^ y) >> 3) & 1); SDL_SetRenderDrawColor(renderer, col[i].r, col[i].g, col[i].b, col[i].a); rect.x = x; rect.y = y; SDL_RenderFillRect(renderer, &rect); } } } int main(int argc, char *argv[]) { SDL_Window *window; SDL_Renderer *renderer; SDL_Texture *texture; Uint32 flags; int i, w, h; int done = 0; int quit = 0; SDL_Event event; const char *saveFile = NULL; /* Check command line usage */ if ( ! argv[1] ) { SDL_Log("Usage: %s [-fullscreen] [-save file.png] ...\n", argv[0]); return(1); } flags = SDL_WINDOW_HIDDEN; for ( i=1; argv[i]; ++i ) { if ( SDL_strcmp(argv[i], "-fullscreen") == 0 ) { SDL_ShowCursor(0); flags |= SDL_WINDOW_FULLSCREEN; } } if (SDL_Init(SDL_INIT_VIDEO) == -1) { SDL_Log("SDL_Init(SDL_INIT_VIDEO) failed: %s\n", SDL_GetError()); return(2); } if (SDL_CreateWindowAndRenderer(0, 0, flags, &window, &renderer) < 0) { SDL_Log("SDL_CreateWindowAndRenderer() failed: %s\n", SDL_GetError()); return(2); } for ( i=1; argv[i]; ++i ) { if ( SDL_strcmp(argv[i], "-fullscreen") == 0 ) { continue; } if ( SDL_strcmp(argv[i], "-quit") == 0 ) { quit = 1; continue; } if ( SDL_strcmp(argv[i], "-save") == 0 && argv[i+1] ) { ++i; saveFile = argv[i]; continue; } /* Open the image file */ texture = IMG_LoadTexture(renderer, argv[i]); if (!texture) { SDL_Log("Couldn't load %s: %s\n", argv[i], SDL_GetError()); continue; } SDL_QueryTexture(texture, NULL, NULL, &w, &h); /* Save the image file, if desired */ if ( saveFile ) { SDL_Surface *surface = IMG_Load(argv[i]); if (surface) { int result; const char *ext = SDL_strrchr(saveFile, '.'); if ( ext && SDL_strcasecmp(ext, ".bmp") == 0 ) { result = SDL_SaveBMP(surface, saveFile); } else if ( ext && SDL_strcasecmp(ext, ".jpg") == 0 ) { result = IMG_SaveJPG(surface, saveFile, 90); } else { result = IMG_SavePNG(surface, saveFile); } if ( result < 0 ) { SDL_Log("Couldn't save %s: %s\n", saveFile, SDL_GetError()); } } else { SDL_Log("Couldn't load %s: %s\n", argv[i], SDL_GetError()); } } /* Show the window */ SDL_SetWindowTitle(window, argv[i]); SDL_SetWindowSize(window, w, h); SDL_ShowWindow(window); done = quit; while ( !done ) { while ( SDL_PollEvent(&event) ) { switch (event.type) { case SDL_KEYUP: switch (event.key.keysym.sym) { case SDLK_LEFT: if ( i > 1 ) { i -= 2; done = 1; } break; case SDLK_RIGHT: if ( argv[i+1] ) { done = 1; } break; case SDLK_ESCAPE: case SDLK_q: argv[i+1] = NULL; /* Drop through to done */ case SDLK_SPACE: case SDLK_TAB: done = 1; break; default: break; } break; case SDL_MOUSEBUTTONDOWN: done = 1; break; case SDL_QUIT: argv[i+1] = NULL; done = 1; break; default: break; } } /* Draw a background pattern in case the image has transparency */ draw_background(renderer, w, h); /* Display the image */ SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); SDL_Delay(100); } SDL_DestroyTexture(texture); } SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); /* We're done! */ SDL_Quit(); return(0); } SDL2_image-2.8.8/include/SDL_image.h0000664000000000000000000021262614761421756013735 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /** * \file SDL_image.h * * Header file for SDL_image library * * A simple library to load images of various formats as SDL surfaces */ #ifndef SDL_IMAGE_H_ #define SDL_IMAGE_H_ #include "SDL.h" #include "SDL_version.h" #include "begin_code.h" /* Set up for C function definitions, even when using C++ */ #ifdef __cplusplus extern "C" { #endif /** * Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL */ #define SDL_IMAGE_MAJOR_VERSION 2 #define SDL_IMAGE_MINOR_VERSION 8 #define SDL_IMAGE_PATCHLEVEL 8 /** * This macro can be used to fill a version structure with the compile-time * version of the SDL_image library. */ #define SDL_IMAGE_VERSION(X) \ { \ (X)->major = SDL_IMAGE_MAJOR_VERSION; \ (X)->minor = SDL_IMAGE_MINOR_VERSION; \ (X)->patch = SDL_IMAGE_PATCHLEVEL; \ } #if SDL_IMAGE_MAJOR_VERSION < 3 && SDL_MAJOR_VERSION < 3 /** * This is the version number macro for the current SDL_image version. * * In versions higher than 2.9.0, the minor version overflows into the * thousands digit: for example, 2.23.0 is encoded as 4300. This macro will * not be available in SDL 3.x or SDL_image 3.x. * * Deprecated, use SDL_IMAGE_VERSION_ATLEAST or SDL_IMAGE_VERSION instead. */ #define SDL_IMAGE_COMPILEDVERSION \ SDL_VERSIONNUM(SDL_IMAGE_MAJOR_VERSION, SDL_IMAGE_MINOR_VERSION, SDL_IMAGE_PATCHLEVEL) #endif /* SDL_IMAGE_MAJOR_VERSION < 3 && SDL_MAJOR_VERSION < 3 */ /** * This macro will evaluate to true if compiled with SDL_image at least X.Y.Z. */ #define SDL_IMAGE_VERSION_ATLEAST(X, Y, Z) \ ((SDL_IMAGE_MAJOR_VERSION >= X) && \ (SDL_IMAGE_MAJOR_VERSION > X || SDL_IMAGE_MINOR_VERSION >= Y) && \ (SDL_IMAGE_MAJOR_VERSION > X || SDL_IMAGE_MINOR_VERSION > Y || SDL_IMAGE_PATCHLEVEL >= Z)) /** * This function gets the version of the dynamically linked SDL_image library. * * it should NOT be used to fill a version structure, instead you should use * the SDL_IMAGE_VERSION() macro. * * \returns SDL_image version. */ extern DECLSPEC const SDL_version * SDLCALL IMG_Linked_Version(void); /** * Initialization flags */ typedef enum IMG_InitFlags { IMG_INIT_JPG = 0x00000001, IMG_INIT_PNG = 0x00000002, IMG_INIT_TIF = 0x00000004, IMG_INIT_WEBP = 0x00000008, IMG_INIT_JXL = 0x00000010, IMG_INIT_AVIF = 0x00000020 } IMG_InitFlags; /** * Initialize SDL_image. * * This function loads dynamic libraries that SDL_image needs, and prepares * them for use. This must be the first function you call in SDL_image, and if * it fails you should not continue with the library. * * Flags should be one or more flags from IMG_InitFlags OR'd together. It * returns the flags successfully initialized, or 0 on failure. * * Currently, these flags are: * * - `IMG_INIT_JPG` * - `IMG_INIT_PNG` * - `IMG_INIT_TIF` * - `IMG_INIT_WEBP` * - `IMG_INIT_JXL` * - `IMG_INIT_AVIF` * * More flags may be added in a future SDL_image release. * * This function may need to load external shared libraries to support various * codecs, which means this function can fail to initialize that support on an * otherwise-reasonable system if the library isn't available; this is not * just a question of exceptional circumstances like running out of memory at * startup! * * Note that you may call this function more than once to initialize with * additional flags. The return value will reflect both new flags that * successfully initialized, and also include flags that had previously been * initialized as well. * * As this will return previously-initialized flags, it's legal to call this * with zero (no flags set). This is a safe no-op that can be used to query * the current initialization state without changing it at all. * * Since this returns previously-initialized flags as well as new ones, and * you can call this with zero, you should not check for a zero return value * to determine an error condition. Instead, you should check to make sure all * the flags you require are set in the return value. If you have a game with * data in a specific format, this might be a fatal error. If you're a generic * image displaying app, perhaps you are fine with only having JPG and PNG * support and can live without WEBP, even if you request support for * everything. * * Unlike other SDL satellite libraries, calls to IMG_Init do not stack; a * single call to IMG_Quit() will deinitialize everything and does not have to * be paired with a matching IMG_Init call. For that reason, it's considered * best practices to have a single IMG_Init and IMG_Quit call in your program. * While this isn't required, be aware of the risks of deviating from that * behavior. * * After initializing SDL_image, the app may begin to load images into * SDL_Surfaces or SDL_Textures. * * \param flags initialization flags, OR'd together. * \returns all currently initialized flags. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_Quit */ extern DECLSPEC int SDLCALL IMG_Init(int flags); /** * Deinitialize SDL_image. * * This should be the last function you call in SDL_image, after freeing all * other resources. This will unload any shared libraries it is using for * various codecs. * * After this call, a call to IMG_Init(0) will return 0 (no codecs loaded). * * You can safely call IMG_Init() to reload various codec support after this * call. * * Unlike other SDL satellite libraries, calls to IMG_Init do not stack; a * single call to IMG_Quit() will deinitialize everything and does not have to * be paired with a matching IMG_Init call. For that reason, it's considered * best practices to have a single IMG_Init and IMG_Quit call in your program. * While this isn't required, be aware of the risks of deviating from that * behavior. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_Init */ extern DECLSPEC void SDLCALL IMG_Quit(void); /** * Load an image from an SDL data source into a software surface. * * An SDL_Surface is a buffer of pixels in memory accessible by the CPU. Use * this if you plan to hand the data to something else or manipulate it * further in code. * * There are no guarantees about what format the new SDL_Surface data will be; * in many cases, SDL_image will attempt to supply a surface that exactly * matches the provided image, but in others it might have to convert (either * because the image is in a format that SDL doesn't directly support or * because it's compressed data that could reasonably uncompress to various * formats and SDL_image had to pick one). You can inspect an SDL_Surface for * its specifics, and use SDL_ConvertSurface to then migrate to any supported * format. * * If the image format supports a transparent pixel, SDL will set the colorkey * for the surface. You can enable RLE acceleration on the surface afterwards * by calling: SDL_SetColorKey(image, SDL_RLEACCEL, image->format->colorkey); * * If `freesrc` is non-zero, the RWops will be closed before returning, * whether this function succeeds or not. SDL_image reads everything it needs * from the RWops during this call in any case. * * Even though this function accepts a file type, SDL_image may still try * other decoders that are capable of detecting file type from the contents of * the image data, but may rely on the caller-provided type string for formats * that it cannot autodetect. If `type` is NULL, SDL_image will rely solely on * its ability to guess the format. * * There is a separate function to read files from disk without having to deal * with SDL_RWops: `IMG_Load("filename.jpg")` will call this function and * manage those details for you, determining the file type from the filename's * extension. * * There is also IMG_Load_RW(), which is equivalent to this function except * that it will rely on SDL_image to determine what type of data it is * loading, much like passing a NULL for type. * * If you are using SDL's 2D rendering API, there is an equivalent call to * load images directly into an SDL_Texture for use by the GPU without using a * software surface: call IMG_LoadTextureTyped_RW() instead. * * When done with the returned surface, the app should dispose of it with a * call to SDL_FreeSurface(). * * \param src an SDL_RWops that data will be read from. * \param freesrc non-zero to close/free the SDL_RWops before returning, zero * to leave it open. * \param type a filename extension that represent this data ("BMP", "GIF", * "PNG", etc). * \returns a new SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_Load * \sa IMG_Load_RW * \sa SDL_FreeSurface */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadTyped_RW(SDL_RWops *src, int freesrc, const char *type); /** * Load an image from a filesystem path into a software surface. * * An SDL_Surface is a buffer of pixels in memory accessible by the CPU. Use * this if you plan to hand the data to something else or manipulate it * further in code. * * There are no guarantees about what format the new SDL_Surface data will be; * in many cases, SDL_image will attempt to supply a surface that exactly * matches the provided image, but in others it might have to convert (either * because the image is in a format that SDL doesn't directly support or * because it's compressed data that could reasonably uncompress to various * formats and SDL_image had to pick one). You can inspect an SDL_Surface for * its specifics, and use SDL_ConvertSurface to then migrate to any supported * format. * * If the image format supports a transparent pixel, SDL will set the colorkey * for the surface. You can enable RLE acceleration on the surface afterwards * by calling: SDL_SetColorKey(image, SDL_RLEACCEL, image->format->colorkey); * * There is a separate function to read files from an SDL_RWops, if you need * an i/o abstraction to provide data from anywhere instead of a simple * filesystem read; that function is IMG_Load_RW(). * * If you are using SDL's 2D rendering API, there is an equivalent call to * load images directly into an SDL_Texture for use by the GPU without using a * software surface: call IMG_LoadTexture() instead. * * When done with the returned surface, the app should dispose of it with a * call to SDL_FreeSurface(). * * \param file a path on the filesystem to load an image from. * \returns a new SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadTyped_RW * \sa IMG_Load_RW * \sa SDL_FreeSurface */ extern DECLSPEC SDL_Surface * SDLCALL IMG_Load(const char *file); /** * Load an image from an SDL data source into a software surface. * * An SDL_Surface is a buffer of pixels in memory accessible by the CPU. Use * this if you plan to hand the data to something else or manipulate it * further in code. * * There are no guarantees about what format the new SDL_Surface data will be; * in many cases, SDL_image will attempt to supply a surface that exactly * matches the provided image, but in others it might have to convert (either * because the image is in a format that SDL doesn't directly support or * because it's compressed data that could reasonably uncompress to various * formats and SDL_image had to pick one). You can inspect an SDL_Surface for * its specifics, and use SDL_ConvertSurface to then migrate to any supported * format. * * If the image format supports a transparent pixel, SDL will set the colorkey * for the surface. You can enable RLE acceleration on the surface afterwards * by calling: SDL_SetColorKey(image, SDL_RLEACCEL, image->format->colorkey); * * If `freesrc` is non-zero, the RWops will be closed before returning, * whether this function succeeds or not. SDL_image reads everything it needs * from the RWops during this call in any case. * * There is a separate function to read files from disk without having to deal * with SDL_RWops: `IMG_Load("filename.jpg")` will call this function and * manage those details for you, determining the file type from the filename's * extension. * * There is also IMG_LoadTyped_RW(), which is equivalent to this function * except a file extension (like "BMP", "JPG", etc) can be specified, in case * SDL_image cannot autodetect the file format. * * If you are using SDL's 2D rendering API, there is an equivalent call to * load images directly into an SDL_Texture for use by the GPU without using a * software surface: call IMG_LoadTexture_RW() instead. * * When done with the returned surface, the app should dispose of it with a * call to SDL_FreeSurface(). * * \param src an SDL_RWops that data will be read from. * \param freesrc non-zero to close/free the SDL_RWops before returning, zero * to leave it open. * \returns a new SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_Load * \sa IMG_LoadTyped_RW * \sa SDL_FreeSurface */ extern DECLSPEC SDL_Surface * SDLCALL IMG_Load_RW(SDL_RWops *src, int freesrc); #if SDL_VERSION_ATLEAST(2,0,0) /** * Load an image from a filesystem path into a GPU texture. * * An SDL_Texture represents an image in GPU memory, usable by SDL's 2D Render * API. This can be significantly more efficient than using a CPU-bound * SDL_Surface if you don't need to manipulate the image directly after * loading it. * * If the loaded image has transparency or a colorkey, a texture with an alpha * channel will be created. Otherwise, SDL_image will attempt to create an * SDL_Texture in the most format that most reasonably represents the image * data (but in many cases, this will just end up being 32-bit RGB or 32-bit * RGBA). * * There is a separate function to read files from an SDL_RWops, if you need * an i/o abstraction to provide data from anywhere instead of a simple * filesystem read; that function is IMG_LoadTexture_RW(). * * If you would rather decode an image to an SDL_Surface (a buffer of pixels * in CPU memory), call IMG_Load() instead. * * When done with the returned texture, the app should dispose of it with a * call to SDL_DestroyTexture(). * * \param renderer the SDL_Renderer to use to create the GPU texture. * \param file a path on the filesystem to load an image from. * \returns a new texture, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadTextureTyped_RW * \sa IMG_LoadTexture_RW * \sa SDL_DestroyTexture */ extern DECLSPEC SDL_Texture * SDLCALL IMG_LoadTexture(SDL_Renderer *renderer, const char *file); /** * Load an image from an SDL data source into a GPU texture. * * An SDL_Texture represents an image in GPU memory, usable by SDL's 2D Render * API. This can be significantly more efficient than using a CPU-bound * SDL_Surface if you don't need to manipulate the image directly after * loading it. * * If the loaded image has transparency or a colorkey, a texture with an alpha * channel will be created. Otherwise, SDL_image will attempt to create an * SDL_Texture in the most format that most reasonably represents the image * data (but in many cases, this will just end up being 32-bit RGB or 32-bit * RGBA). * * If `freesrc` is non-zero, the RWops will be closed before returning, * whether this function succeeds or not. SDL_image reads everything it needs * from the RWops during this call in any case. * * There is a separate function to read files from disk without having to deal * with SDL_RWops: `IMG_LoadTexture(renderer, "filename.jpg")` will call this * function and manage those details for you, determining the file type from * the filename's extension. * * There is also IMG_LoadTextureTyped_RW(), which is equivalent to this * function except a file extension (like "BMP", "JPG", etc) can be specified, * in case SDL_image cannot autodetect the file format. * * If you would rather decode an image to an SDL_Surface (a buffer of pixels * in CPU memory), call IMG_Load() instead. * * When done with the returned texture, the app should dispose of it with a * call to SDL_DestroyTexture(). * * \param renderer the SDL_Renderer to use to create the GPU texture. * \param src an SDL_RWops that data will be read from. * \param freesrc non-zero to close/free the SDL_RWops before returning, zero * to leave it open. * \returns a new texture, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadTexture * \sa IMG_LoadTextureTyped_RW * \sa SDL_DestroyTexture */ extern DECLSPEC SDL_Texture * SDLCALL IMG_LoadTexture_RW(SDL_Renderer *renderer, SDL_RWops *src, int freesrc); /** * Load an image from an SDL data source into a GPU texture. * * An SDL_Texture represents an image in GPU memory, usable by SDL's 2D Render * API. This can be significantly more efficient than using a CPU-bound * SDL_Surface if you don't need to manipulate the image directly after * loading it. * * If the loaded image has transparency or a colorkey, a texture with an alpha * channel will be created. Otherwise, SDL_image will attempt to create an * SDL_Texture in the most format that most reasonably represents the image * data (but in many cases, this will just end up being 32-bit RGB or 32-bit * RGBA). * * If `freesrc` is non-zero, the RWops will be closed before returning, * whether this function succeeds or not. SDL_image reads everything it needs * from the RWops during this call in any case. * * Even though this function accepts a file type, SDL_image may still try * other decoders that are capable of detecting file type from the contents of * the image data, but may rely on the caller-provided type string for formats * that it cannot autodetect. If `type` is NULL, SDL_image will rely solely on * its ability to guess the format. * * There is a separate function to read files from disk without having to deal * with SDL_RWops: `IMG_LoadTexture("filename.jpg")` will call this function * and manage those details for you, determining the file type from the * filename's extension. * * There is also IMG_LoadTexture_RW(), which is equivalent to this function * except that it will rely on SDL_image to determine what type of data it is * loading, much like passing a NULL for type. * * If you would rather decode an image to an SDL_Surface (a buffer of pixels * in CPU memory), call IMG_LoadTyped_RW() instead. * * When done with the returned texture, the app should dispose of it with a * call to SDL_DestroyTexture(). * * \param renderer the SDL_Renderer to use to create the GPU texture. * \param src an SDL_RWops that data will be read from. * \param freesrc non-zero to close/free the SDL_RWops before returning, zero * to leave it open. * \param type a filename extension that represent this data ("BMP", "GIF", * "PNG", etc). * \returns a new texture, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadTexture * \sa IMG_LoadTexture_RW * \sa SDL_DestroyTexture */ extern DECLSPEC SDL_Texture * SDLCALL IMG_LoadTextureTyped_RW(SDL_Renderer *renderer, SDL_RWops *src, int freesrc, const char *type); #endif /* SDL 2.0 */ /** * Detect AVIF image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is AVIF data, zero otherwise. * * \since This function is available since SDL_image 2.6.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isAVIF(SDL_RWops *src); /** * Detect ICO image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is ICO data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isICO(SDL_RWops *src); /** * Detect CUR image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is CUR data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isCUR(SDL_RWops *src); /** * Detect BMP image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is BMP data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isBMP(SDL_RWops *src); /** * Detect GIF image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is GIF data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isGIF(SDL_RWops *src); /** * Detect JPG image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is JPG data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isJPG(SDL_RWops *src); /** * Detect JXL image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is JXL data, zero otherwise. * * \since This function is available since SDL_image 2.6.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isJXL(SDL_RWops *src); /** * Detect LBM image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is LBM data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isLBM(SDL_RWops *src); /** * Detect PCX image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is PCX data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isPCX(SDL_RWops *src); /** * Detect PNG image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is PNG data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isPNG(SDL_RWops *src); /** * Detect PNM image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is PNM data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isPNM(SDL_RWops *src); /** * Detect SVG image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is SVG data, zero otherwise. * * \since This function is available since SDL_image 2.0.2. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isSVG(SDL_RWops *src); /** * Detect QOI image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is QOI data, zero otherwise. * * \since This function is available since SDL_image 2.6.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isQOI(SDL_RWops *src); /** * Detect TIFF image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is TIFF data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isTIF(SDL_RWops *src); /** * Detect XCF image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is XCF data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXPM * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isXCF(SDL_RWops *src); /** * Detect XPM image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is XPM data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXV * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isXPM(SDL_RWops *src); /** * Detect XV image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is XV data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isWEBP */ extern DECLSPEC int SDLCALL IMG_isXV(SDL_RWops *src); /** * Detect WEBP image data on a readable/seekable SDL_RWops. * * This function attempts to determine if a file is a given filetype, reading * the least amount possible from the SDL_RWops (usually a few bytes). * * There is no distinction made between "not the filetype in question" and * basic i/o errors. * * This function will always attempt to seek the RWops back to where it * started when this function was called, but it will not report any errors in * doing so, but assuming seeking works, this means you can immediately use * this with a different IMG_isTYPE function, or load the image without * further seeking. * * You do not need to call this function to load data; SDL_image can work to * determine file type in many cases in its standard load functions. * * \param src a seekable/readable SDL_RWops to provide image data. * \returns non-zero if this is WEBP data, zero otherwise. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_isAVIF * \sa IMG_isICO * \sa IMG_isCUR * \sa IMG_isBMP * \sa IMG_isGIF * \sa IMG_isJPG * \sa IMG_isJXL * \sa IMG_isLBM * \sa IMG_isPCX * \sa IMG_isPNG * \sa IMG_isPNM * \sa IMG_isSVG * \sa IMG_isQOI * \sa IMG_isTIF * \sa IMG_isXCF * \sa IMG_isXPM * \sa IMG_isXV */ extern DECLSPEC int SDLCALL IMG_isWEBP(SDL_RWops *src); /** * Load a AVIF image directly. * * If you know you definitely have a AVIF image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.6.0. * * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadAVIF_RW(SDL_RWops *src); /** * Load a ICO image directly. * * If you know you definitely have a ICO image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadICO_RW(SDL_RWops *src); /** * Load a CUR image directly. * * If you know you definitely have a CUR image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadCUR_RW(SDL_RWops *src); /** * Load a BMP image directly. * * If you know you definitely have a BMP image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadBMP_RW(SDL_RWops *src); /** * Load a GIF image directly. * * If you know you definitely have a GIF image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadGIF_RW(SDL_RWops *src); /** * Load a JPG image directly. * * If you know you definitely have a JPG image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadJPG_RW(SDL_RWops *src); /** * Load a JXL image directly. * * If you know you definitely have a JXL image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.6.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadJXL_RW(SDL_RWops *src); /** * Load a LBM image directly. * * If you know you definitely have a LBM image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadLBM_RW(SDL_RWops *src); /** * Load a PCX image directly. * * If you know you definitely have a PCX image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadPCX_RW(SDL_RWops *src); /** * Load a PNG image directly. * * If you know you definitely have a PNG image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadPNG_RW(SDL_RWops *src); /** * Load a PNM image directly. * * If you know you definitely have a PNM image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadPNM_RW(SDL_RWops *src); /** * Load a SVG image directly. * * If you know you definitely have a SVG image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.2. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadSVG_RW(SDL_RWops *src); /** * Load a QOI image directly. * * If you know you definitely have a QOI image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.6.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadQOI_RW(SDL_RWops *src); /** * Load a TGA image directly. * * If you know you definitely have a TGA image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadTGA_RW(SDL_RWops *src); /** * Load a TIFF image directly. * * If you know you definitely have a TIFF image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadTIF_RW(SDL_RWops *src); /** * Load a XCF image directly. * * If you know you definitely have a XCF image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadXCF_RW(SDL_RWops *src); /** * Load a XPM image directly. * * If you know you definitely have a XPM image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXV_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadXPM_RW(SDL_RWops *src); /** * Load a XV image directly. * * If you know you definitely have a XV image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadWEBP_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadXV_RW(SDL_RWops *src); /** * Load a WEBP image directly. * * If you know you definitely have a WEBP image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops to load image data from. * \returns SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_LoadAVIF_RW * \sa IMG_LoadICO_RW * \sa IMG_LoadCUR_RW * \sa IMG_LoadBMP_RW * \sa IMG_LoadGIF_RW * \sa IMG_LoadJPG_RW * \sa IMG_LoadJXL_RW * \sa IMG_LoadLBM_RW * \sa IMG_LoadPCX_RW * \sa IMG_LoadPNG_RW * \sa IMG_LoadPNM_RW * \sa IMG_LoadSVG_RW * \sa IMG_LoadQOI_RW * \sa IMG_LoadTGA_RW * \sa IMG_LoadTIF_RW * \sa IMG_LoadXCF_RW * \sa IMG_LoadXPM_RW * \sa IMG_LoadXV_RW */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadWEBP_RW(SDL_RWops *src); /** * Load an SVG image, scaled to a specific size. * * Since SVG files are resolution-independent, you specify the size you would * like the output image to be and it will be generated at those dimensions. * * Either width or height may be 0 and the image will be auto-sized to * preserve aspect ratio. * * When done with the returned surface, the app should dispose of it with a * call to SDL_FreeSurface(). * * \param src an SDL_RWops to load SVG data from. * \param width desired width of the generated surface, in pixels. * \param height desired height of the generated surface, in pixels. * \returns a new SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.6.0. */ extern DECLSPEC SDL_Surface * SDLCALL IMG_LoadSizedSVG_RW(SDL_RWops *src, int width, int height); /** * Load an XPM image from a memory array. * * The returned surface will be an 8bpp indexed surface, if possible, * otherwise it will be 32bpp. If you always want 32-bit data, use * IMG_ReadXPMFromArrayToRGB888() instead. * * When done with the returned surface, the app should dispose of it with a * call to SDL_FreeSurface(). * * \param xpm a null-terminated array of strings that comprise XPM data. * \returns a new SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_ReadXPMFromArrayToRGB888 */ extern DECLSPEC SDL_Surface * SDLCALL IMG_ReadXPMFromArray(char **xpm); /** * Load an XPM image from a memory array. * * The returned surface will always be a 32-bit RGB surface. If you want 8-bit * indexed colors (and the XPM data allows it), use IMG_ReadXPMFromArray() * instead. * * When done with the returned surface, the app should dispose of it with a * call to SDL_FreeSurface(). * * \param xpm a null-terminated array of strings that comprise XPM data. * \returns a new SDL surface, or NULL on error. * * \since This function is available since SDL_image 2.6.0. * * \sa IMG_ReadXPMFromArray */ extern DECLSPEC SDL_Surface * SDLCALL IMG_ReadXPMFromArrayToRGB888(char **xpm); /** * Save an SDL_Surface into a PNG image file. * * If the file already exists, it will be overwritten. * * \param surface the SDL surface to save. * \param file path on the filesystem to write new file to. * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_SavePNG_RW * \sa IMG_SaveJPG * \sa IMG_SaveJPG_RW */ extern DECLSPEC int SDLCALL IMG_SavePNG(SDL_Surface *surface, const char *file); /** * Save an SDL_Surface into PNG image data, via an SDL_RWops. * * If you just want to save to a filename, you can use IMG_SavePNG() instead. * * \param surface the SDL surface to save. * \param dst the SDL_RWops to save the image data to. * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_image 2.0.0. * * \sa IMG_SavePNG * \sa IMG_SaveJPG * \sa IMG_SaveJPG_RW */ extern DECLSPEC int SDLCALL IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst); /** * Save an SDL_Surface into a JPEG image file. * * If the file already exists, it will be overwritten. * * \param surface the SDL surface to save. * \param file path on the filesystem to write new file to. * \param quality [0; 33] is Lowest quality, [34; 66] is Middle quality, [67; * 100] is Highest quality. * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_image 2.0.2. * * \sa IMG_SaveJPG_RW * \sa IMG_SavePNG * \sa IMG_SavePNG_RW */ extern DECLSPEC int SDLCALL IMG_SaveJPG(SDL_Surface *surface, const char *file, int quality); /** * Save an SDL_Surface into JPEG image data, via an SDL_RWops. * * If you just want to save to a filename, you can use IMG_SaveJPG() instead. * * \param surface the SDL surface to save. * \param dst the SDL_RWops to save the image data to. * \returns 0 if successful, -1 on error. * * \since This function is available since SDL_image 2.0.2. * * \sa IMG_SaveJPG * \sa IMG_SavePNG * \sa IMG_SavePNG_RW */ extern DECLSPEC int SDLCALL IMG_SaveJPG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst, int quality); /** * Animated image support. * * Currently only animated GIFs are supported. */ typedef struct IMG_Animation { int w, h; int count; SDL_Surface **frames; int *delays; } IMG_Animation; /** * Load an animation from a file. * * When done with the returned animation, the app should dispose of it with a * call to IMG_FreeAnimation(). * * \param file path on the filesystem containing an animated image. * \returns a new IMG_Animation, or NULL on error. * * \since This function is available since SDL_image 2.6.0. * * \sa IMG_FreeAnimation */ extern DECLSPEC IMG_Animation * SDLCALL IMG_LoadAnimation(const char *file); /** * Load an animation from an SDL_RWops. * * If `freesrc` is non-zero, the RWops will be closed before returning, * whether this function succeeds or not. SDL_image reads everything it needs * from the RWops during this call in any case. * * When done with the returned animation, the app should dispose of it with a * call to IMG_FreeAnimation(). * * \param src an SDL_RWops that data will be read from. * \param freesrc non-zero to close/free the SDL_RWops before returning, zero * to leave it open. * \returns a new IMG_Animation, or NULL on error. * * \since This function is available since SDL_image 2.6.0. * * \sa IMG_FreeAnimation */ extern DECLSPEC IMG_Animation * SDLCALL IMG_LoadAnimation_RW(SDL_RWops *src, int freesrc); /** * Load an animation from an SDL datasource * * Even though this function accepts a file type, SDL_image may still try * other decoders that are capable of detecting file type from the contents of * the image data, but may rely on the caller-provided type string for formats * that it cannot autodetect. If `type` is NULL, SDL_image will rely solely on * its ability to guess the format. * * If `freesrc` is non-zero, the RWops will be closed before returning, * whether this function succeeds or not. SDL_image reads everything it needs * from the RWops during this call in any case. * * When done with the returned animation, the app should dispose of it with a * call to IMG_FreeAnimation(). * * \param src an SDL_RWops that data will be read from. * \param freesrc non-zero to close/free the SDL_RWops before returning, zero * to leave it open. * \param type a filename extension that represent this data ("GIF", etc). * \returns a new IMG_Animation, or NULL on error. * * \since This function is available since SDL_image 2.6.0. * * \sa IMG_LoadAnimation * \sa IMG_LoadAnimation_RW * \sa IMG_FreeAnimation */ extern DECLSPEC IMG_Animation * SDLCALL IMG_LoadAnimationTyped_RW(SDL_RWops *src, int freesrc, const char *type); /** * Dispose of an IMG_Animation and free its resources. * * The provided `anim` pointer is not valid once this call returns. * * \param anim IMG_Animation to dispose of. * * \since This function is available since SDL_image 2.6.0. * * \sa IMG_LoadAnimation * \sa IMG_LoadAnimation_RW * \sa IMG_LoadAnimationTyped_RW */ extern DECLSPEC void SDLCALL IMG_FreeAnimation(IMG_Animation *anim); /** * Load a GIF animation directly. * * If you know you definitely have a GIF image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops that data will be read from. * \returns a new IMG_Animation, or NULL on error. * * \since This function is available since SDL_image 2.6.0. * * \sa IMG_LoadAnimation * \sa IMG_LoadAnimation_RW * \sa IMG_LoadAnimationTyped_RW * \sa IMG_FreeAnimation */ extern DECLSPEC IMG_Animation * SDLCALL IMG_LoadGIFAnimation_RW(SDL_RWops *src); /** * Load a WEBP animation directly. * * If you know you definitely have a WEBP image, you can call this function, * which will skip SDL_image's file format detection routines. Generally it's * better to use the abstract interfaces; also, there is only an SDL_RWops * interface available here. * * \param src an SDL_RWops that data will be read from. * \returns a new IMG_Animation, or NULL on error. * * \since This function is available since SDL_image 2.6.0. * * \sa IMG_LoadAnimation * \sa IMG_LoadAnimation_RW * \sa IMG_LoadAnimationTyped_RW * \sa IMG_FreeAnimation */ extern DECLSPEC IMG_Animation * SDLCALL IMG_LoadWEBPAnimation_RW(SDL_RWops *src); /** * Report SDL_image errors * * \sa IMG_GetError */ #define IMG_SetError SDL_SetError /** * Get last SDL_image error * * \sa IMG_SetError */ #define IMG_GetError SDL_GetError /* Ends C function definitions when using C++ */ #ifdef __cplusplus } #endif #include "close_code.h" #endif /* SDL_IMAGE_H_ */ SDL2_image-2.8.8/mingw/pkg-support/Makefile0000664000000000000000000000245114757635025015423 0ustar00# # Makefile for installing the mingw32 version of the SDL2_image library CROSS_PATH := /usr/local ARCHITECTURES := i686-w64-mingw32 x86_64-w64-mingw32 all install: @echo "Type \"make native\" to install 32-bit to /usr" @echo "Type \"make cross\" to install 32-bit and 64-bit to $(CROSS_PATH)" native: make install-package arch=i686-w64-mingw32 prefix=/usr cross: mkdir -p $(CROSS_PATH)/cmake cp -rv cmake/* $(CROSS_PATH)/cmake for arch in $(ARCHITECTURES); do \ make install-package arch=$$arch prefix=$(CROSS_PATH)/$$arch; \ done install-package: @if test -d $(arch) && test -d $(prefix); then \ (cd $(arch) && cp -rv bin include lib $(prefix)/); \ sed "s|^prefix=.*|prefix=$(prefix)|" <$(arch)/lib/pkgconfig/SDL2_image.pc >$(prefix)/lib/pkgconfig/SDL2_image.pc; \ sed "s|^libdir=.*|libdir=\'$(prefix)/lib\'|" <$(arch)/lib/libSDL2_image.la >$(prefix)/lib/libSDL2_image.la; \ sed -e "s|^set[(]bindir \".*|set(bindir \"$(prefix)/bin\")|" \ -e "s|^set[(]includedir \".*|set(includedir \"$(prefix)/include\")|" \ -e "s|^set[(]libdir \".*|set(libdir \"$(prefix)/lib\")|" <$(arch)/lib/cmake/SDL2_image/sdl2_image-config.cmake >$(prefix)/lib/cmake/SDL2_image/sdl2_image-config.cmake; \ else \ echo "*** ERROR: $(arch) or $(prefix) does not exist!"; \ exit 1; \ fi SDL2_image-2.8.8/mingw/pkg-support/cmake/sdl2_image-config-version.cmake0000664000000000000000000000147014247536537023003 0ustar00# SDL2_image CMake version configuration file: # This file is meant to be placed in a cmake subfolder of SDL2_image-devel-2.x.y-mingw if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(sdl2_image_config_path "${CMAKE_CURRENT_LIST_DIR}/../i686-w64-mingw32/lib/cmake/SDL2_image/sdl2_image-config-version.cmake") elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) set(sdl2_image_config_path "${CMAKE_CURRENT_LIST_DIR}/../x86_64-w64-mingw32/lib/cmake/SDL2_image/sdl2_image-config-version.cmake") else("${CMAKE_SIZEOF_VOID_P}" STREQUAL "") set(PACKAGE_VERSION_UNSUITABLE TRUE) return() endif() if(NOT EXISTS "${sdl2_image_config_path}") message(WARNING "${sdl2_image_config_path} does not exist: MinGW development package is corrupted") set(PACKAGE_VERSION_UNSUITABLE TRUE) return() endif() include("${sdl2_image_config_path}") SDL2_image-2.8.8/mingw/pkg-support/cmake/sdl2_image-config.cmake0000664000000000000000000000141614256665145021316 0ustar00# SDL2_image CMake configuration file: # This file is meant to be placed in a cmake subfolder of SDL2_image-devel-2.x.y-mingw if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(sdl2_image_config_path "${CMAKE_CURRENT_LIST_DIR}/../i686-w64-mingw32/lib/cmake/SDL2_image/sdl2_image-config.cmake") elseif(CMAKE_SIZEOF_VOID_P EQUAL 8) set(sdl2_image_config_path "${CMAKE_CURRENT_LIST_DIR}/../x86_64-w64-mingw32/lib/cmake/SDL2_image/sdl2_image-config.cmake") else("${CMAKE_SIZEOF_VOID_P}" STREQUAL "") set(SDL2_image_FOUND FALSE) return() endif() if(NOT EXISTS "${sdl2_image_config_path}") message(WARNING "${sdl2_image_config_path} does not exist: MinGW development package is corrupted") set(SDL2_image_FOUND FALSE) return() endif() include("${sdl2_image_config_path}") SDL2_image-2.8.8/sdl2_image-config-version.cmake.in0000664000000000000000000000061014506641312016671 0ustar00# sdl2_image cmake project-config-version input for ./configure scripts set(PACKAGE_VERSION "@MAJOR_VERSION@.@MINOR_VERSION@.@MICRO_VERSION@") if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) set(PACKAGE_VERSION_COMPATIBLE FALSE) else() set(PACKAGE_VERSION_COMPATIBLE TRUE) if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) set(PACKAGE_VERSION_EXACT TRUE) endif() endif() SDL2_image-2.8.8/sdl2_image-config.cmake.in0000664000000000000000000000770214751444605015227 0ustar00# sdl2_image cmake project-config input for ./configure scripts include(FeatureSummary) set_package_properties(SDL2_image PROPERTIES URL "https://www.libsdl.org/projects/SDL_image/" DESCRIPTION "SDL_image is an image file loading library" ) set(SDL2_image_FOUND TRUE) set(SDL2IMAGE_AVIF @LOAD_AVIF@) set(SDL2IMAGE_BMP @LOAD_BMP@) set(SDL2IMAGE_GIF @LOAD_GIF@) set(SDL2IMAGE_JPG @LOAD_JPG@) set(SDL2IMAGE_JXL @LOAD_JXL@) set(SDL2IMAGE_LBM @LOAD_LBM@) set(SDL2IMAGE_PCX @LOAD_PCX@) set(SDL2IMAGE_PNG @LOAD_PNG@) set(SDL2IMAGE_PNM @LOAD_PNM@) set(SDL2IMAGE_QOI @LOAD_QOI@) set(SDL2IMAGE_SVG @LOAD_SVG@) set(SDL2IMAGE_TGA @LOAD_TGA@) set(SDL2IMAGE_TIF @LOAD_TIF@) set(SDL2IMAGE_XCF @LOAD_XCF@) set(SDL2IMAGE_XPM @LOAD_XPM@) set(SDL2IMAGE_XV @LOAD_XV@) set(SDL2IMAGE_WEBP @LOAD_WEBP@) set(SDL2IMAGE_JPG_SAVE @SDL2IMAGE_JPG_SAVE@) set(SDL2IMAGE_PNG_SAVE @SDL2IMAGE_PNG_SAVE@) set(SDL2IMAGE_VENDORED FALSE) set(SDL2IMAGE_BACKEND_IMAGEIO @USE_IMAGEIO@) set(SDL2IMAGE_BACKEND_STB @USE_STBIMAGE@) set(SDL2IMAGE_BACKEND_WIC @USE_WIC@) get_filename_component(CMAKE_CURRENT_LIST_DIR ${CMAKE_CURRENT_LIST_DIR} REALPATH) get_filename_component(prefix "${CMAKE_CURRENT_LIST_DIR}/@cmake_prefix_relpath@" ABSOLUTE) set(exec_prefix "@exec_prefix@") set(bindir "@bindir@") set(includedir "@includedir@") set(libdir "@libdir@") set(_sdl2image_extra_static_libraries "@CMAKE_LIBS@") string(REGEX REPLACE "(^[;]+|[;]+$)" "" _sdl2image_extra_static_libraries "${_sdl2image_extra_static_libraries}") set(_sdl2image_bindir "${bindir}") set(_sdl2image_libdir "${libdir}") set(_sdl2image_incdir "${includedir}/SDL2") include(CMakeFindDependencyMacro) set(_original_cmake_module_path "${CMAKE_MODULE_PATH}") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") if("WebP::webp" IN_LIST _sdl2image_extra_static_libraries) find_dependency(webp) endif() if("avif" IN_LIST _sdl2image_extra_static_libraries) find_dependency(libavif) endif() if("TIFF::TIFF" IN_LIST _sdl2image_extra_static_libraries) find_dependency(TIFF) endif() if("JPEG::JPEG" IN_LIST _sdl2image_extra_static_libraries) find_dependency(JPEG) endif() if("libjxl::libjxl" IN_LIST _sdl2image_extra_static_libraries) find_dependency(libjxl) endif() if("PNG::PNG" IN_LIST _sdl2image_extra_static_libraries) find_dependency(PNG) endif() set(CMAKE_MODULE_PATH "${_original_cmake_module_path}") unset(_original_cmake_module_path) unset(prefix) unset(exec_prefix) unset(bindir) unset(includedir) unset(libdir) include(CMakeFindDependencyMacro) if(NOT TARGET SDL2_image::SDL2_image) add_library(SDL2_image::SDL2_image SHARED IMPORTED) set_target_properties(SDL2_image::SDL2_image PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_sdl2image_incdir}" COMPATIBLE_INTERFACE_BOOL "SDL2_SHARED" INTERFACE_SDL2_SHARED "ON" ) if(WIN32) set_target_properties(SDL2_image::SDL2_image PROPERTIES IMPORTED_LOCATION "${_sdl2image_bindir}/SDL2_image.dll" IMPORTED_IMPLIB "${_sdl2image_libdir}/${CMAKE_STATIC_LIBRARY_PREFIX}SDL2_image.dll${CMAKE_STATIC_LIBRARY_SUFFIX}" ) else() set_target_properties(SDL2_image::SDL2_image PROPERTIES IMPORTED_LOCATION "${_sdl2image_libdir}/${CMAKE_SHARED_LIBRARY_PREFIX}SDL2_image${CMAKE_SHARED_LIBRARY_SUFFIX}" ) endif() endif() if(NOT TARGET SDL2_image::SDL2_image-static) add_library(SDL2_image::SDL2_image-static STATIC IMPORTED) set_target_properties(SDL2_image::SDL2_image-static PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_sdl2image_incdir}" IMPORTED_LOCATION "${_sdl2image_libdir}/${CMAKE_STATIC_LIBRARY_PREFIX}SDL2_image${CMAKE_STATIC_LIBRARY_SUFFIX}" INTERFACE_LINK_LIBRARIES "${_sdl2image_extra_static_libraries}" ) endif() unset(_sdl2image_extra_static_libraries) unset(_sdl2image_bindir) unset(_sdl2image_libdir) unset(_sdl2image_incdir) SDL2_image-2.8.8/src/IMG.c0000664000000000000000000003153614751445211011712 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* A simple library to load images of various formats as SDL surfaces */ #include "SDL_image.h" #ifdef __EMSCRIPTEN__ #include #endif #if defined(SDL_BUILD_MAJOR_VERSION) && defined(SDL_COMPILE_TIME_ASSERT) SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MAJOR_VERSION, SDL_IMAGE_MAJOR_VERSION == SDL_BUILD_MAJOR_VERSION); SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MINOR_VERSION, SDL_IMAGE_MINOR_VERSION == SDL_BUILD_MINOR_VERSION); SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MICRO_VERSION, SDL_IMAGE_PATCHLEVEL == SDL_BUILD_MICRO_VERSION); #endif #if defined(SDL_COMPILE_TIME_ASSERT) SDL_COMPILE_TIME_ASSERT(SDL_IMAGE_MAJOR_VERSION_min, SDL_IMAGE_MAJOR_VERSION >= 0); /* Limited only by the need to fit in SDL_version */ SDL_COMPILE_TIME_ASSERT(SDL_IMAGE_MAJOR_VERSION_max, SDL_IMAGE_MAJOR_VERSION <= 255); SDL_COMPILE_TIME_ASSERT(SDL_IMAGE_MINOR_VERSION_min, SDL_IMAGE_MINOR_VERSION >= 0); /* Limited only by the need to fit in SDL_version */ SDL_COMPILE_TIME_ASSERT(SDL_IMAGE_MINOR_VERSION_max, SDL_IMAGE_MINOR_VERSION <= 255); SDL_COMPILE_TIME_ASSERT(SDL_IMAGE_PATCHLEVEL_min, SDL_IMAGE_PATCHLEVEL >= 0); /* Limited by its encoding in SDL_VERSIONNUM and in the ABI versions */ SDL_COMPILE_TIME_ASSERT(SDL_IMAGE_PATCHLEVEL_max, SDL_IMAGE_PATCHLEVEL <= 99); #endif /* Table of image detection and loading functions */ static struct { const char *type; int (SDLCALL *is)(SDL_RWops *src); SDL_Surface *(SDLCALL *load)(SDL_RWops *src); } supported[] = { /* keep magicless formats first */ { "TGA", NULL, IMG_LoadTGA_RW }, { "AVIF",IMG_isAVIF,IMG_LoadAVIF_RW }, { "CUR", IMG_isCUR, IMG_LoadCUR_RW }, { "ICO", IMG_isICO, IMG_LoadICO_RW }, { "BMP", IMG_isBMP, IMG_LoadBMP_RW }, { "GIF", IMG_isGIF, IMG_LoadGIF_RW }, { "JPG", IMG_isJPG, IMG_LoadJPG_RW }, { "JXL", IMG_isJXL, IMG_LoadJXL_RW }, { "LBM", IMG_isLBM, IMG_LoadLBM_RW }, { "PCX", IMG_isPCX, IMG_LoadPCX_RW }, { "PNG", IMG_isPNG, IMG_LoadPNG_RW }, { "PNM", IMG_isPNM, IMG_LoadPNM_RW }, /* P[BGP]M share code */ { "SVG", IMG_isSVG, IMG_LoadSVG_RW }, { "TIF", IMG_isTIF, IMG_LoadTIF_RW }, { "XCF", IMG_isXCF, IMG_LoadXCF_RW }, { "XPM", IMG_isXPM, IMG_LoadXPM_RW }, { "XV", IMG_isXV, IMG_LoadXV_RW }, { "WEBP", IMG_isWEBP, IMG_LoadWEBP_RW }, { "QOI", IMG_isQOI, IMG_LoadQOI_RW }, }; /* Table of animation detection and loading functions */ static struct { const char *type; int (SDLCALL *is)(SDL_RWops *src); IMG_Animation *(SDLCALL *load)(SDL_RWops *src); } supported_anims[] = { /* keep magicless formats first */ { "GIF", IMG_isGIF, IMG_LoadGIFAnimation_RW }, { "WEBP", IMG_isWEBP, IMG_LoadWEBPAnimation_RW }, }; const SDL_version *IMG_Linked_Version(void) { static SDL_version linked_version; SDL_IMAGE_VERSION(&linked_version); return(&linked_version); } extern int IMG_InitAVIF(void); extern void IMG_QuitAVIF(void); extern int IMG_InitJPG(void); extern void IMG_QuitJPG(void); extern int IMG_InitJXL(void); extern void IMG_QuitJXL(void); extern int IMG_InitPNG(void); extern void IMG_QuitPNG(void); extern int IMG_InitTIF(void); extern void IMG_QuitTIF(void); extern int IMG_InitWEBP(void); extern void IMG_QuitWEBP(void); static int initialized = 0; int IMG_Init(int flags) { int result = 0; if (flags & IMG_INIT_AVIF) { if ((initialized & IMG_INIT_AVIF) || IMG_InitAVIF() == 0) { result |= IMG_INIT_AVIF; } } if (flags & IMG_INIT_JPG) { if ((initialized & IMG_INIT_JPG) || IMG_InitJPG() == 0) { result |= IMG_INIT_JPG; } } if (flags & IMG_INIT_JXL) { if ((initialized & IMG_INIT_JXL) || IMG_InitJXL() == 0) { result |= IMG_INIT_JXL; } } if (flags & IMG_INIT_PNG) { if ((initialized & IMG_INIT_PNG) || IMG_InitPNG() == 0) { result |= IMG_INIT_PNG; } } if (flags & IMG_INIT_TIF) { if ((initialized & IMG_INIT_TIF) || IMG_InitTIF() == 0) { result |= IMG_INIT_TIF; } } if (flags & IMG_INIT_WEBP) { if ((initialized & IMG_INIT_WEBP) || IMG_InitWEBP() == 0) { result |= IMG_INIT_WEBP; } } initialized |= result; return initialized; } void IMG_Quit() { if (initialized & IMG_INIT_AVIF) { IMG_QuitAVIF(); } if (initialized & IMG_INIT_JPG) { IMG_QuitJPG(); } if (initialized & IMG_INIT_JXL) { IMG_QuitJXL(); } if (initialized & IMG_INIT_PNG) { IMG_QuitPNG(); } if (initialized & IMG_INIT_TIF) { IMG_QuitTIF(); } if (initialized & IMG_INIT_WEBP) { IMG_QuitWEBP(); } initialized = 0; } #if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) /* Load an image from a file */ SDL_Surface *IMG_Load(const char *file) { #if __EMSCRIPTEN__ int w, h; char *data; SDL_Surface *surf; data = emscripten_get_preloaded_image_data(file, &w, &h); if (data != NULL) { surf = SDL_CreateRGBSurfaceWithFormat(0, w, h, 0, SDL_PIXELFORMAT_ABGR8888); if (surf != NULL) { memcpy(surf->pixels, data, w * h * 4); } free(data); return surf; } #endif SDL_RWops *src = SDL_RWFromFile(file, "rb"); const char *ext = SDL_strrchr(file, '.'); if (ext) { ext++; } if (!src) { /* The error message has been set in SDL_RWFromFile */ return NULL; } return IMG_LoadTyped_RW(src, 1, ext); } #endif /* Load an image from an SDL datasource (for compatibility) */ SDL_Surface *IMG_Load_RW(SDL_RWops *src, int freesrc) { return IMG_LoadTyped_RW(src, freesrc, NULL); } /* Portable case-insensitive string compare function */ static int IMG_string_equals(const char *str1, const char *str2) { while ( *str1 && *str2 ) { if ( SDL_toupper((unsigned char)*str1) != SDL_toupper((unsigned char)*str2) ) break; ++str1; ++str2; } return (!*str1 && !*str2); } /* Load an image from an SDL datasource, optionally specifying the type */ SDL_Surface *IMG_LoadTyped_RW(SDL_RWops *src, int freesrc, const char *type) { int i; SDL_Surface *image; /* Make sure there is something to do.. */ if ( src == NULL ) { IMG_SetError("Passed a NULL data source"); return(NULL); } /* See whether or not this data source can handle seeking */ if ( SDL_RWseek(src, 0, RW_SEEK_CUR) < 0 ) { IMG_SetError("Can't seek in this data source"); if (freesrc) SDL_RWclose(src); return(NULL); } #ifdef __EMSCRIPTEN__ /*load through preloadedImages*/ if ( src->type == SDL_RWOPS_STDFILE ) { int w, h, success; char *data; SDL_Surface *surf; data = emscripten_get_preloaded_image_data_from_FILE(src->hidden.stdio.fp, &w, &h); if (data) { surf = SDL_CreateRGBSurfaceWithFormat(0, w, h, 0, SDL_PIXELFORMAT_ABGR8888); if (surf != NULL) { memcpy(surf->pixels, data, w * h * 4); } free(data); if (freesrc) SDL_RWclose(src); /* If SDL_CreateRGBSurfaceWithFormat returns NULL, it has set the error message for us */ return surf; } } #endif /* Detect the type of image being loaded */ for ( i=0; i < SDL_arraysize(supported); ++i ) { if (supported[i].is) { if (!supported[i].is(src)) continue; } else { /* magicless format */ if (!type || !IMG_string_equals(type, supported[i].type)) continue; } #ifdef DEBUG_IMGLIB fprintf(stderr, "IMGLIB: Loading image as %s\n", supported[i].type); #endif image = supported[i].load(src); if (freesrc) SDL_RWclose(src); return image; } if ( freesrc ) { SDL_RWclose(src); } IMG_SetError("Unsupported image format"); return NULL; } #if SDL_VERSION_ATLEAST(2,0,0) SDL_Texture *IMG_LoadTexture(SDL_Renderer *renderer, const char *file) { SDL_Texture *texture = NULL; SDL_Surface *surface = IMG_Load(file); if (surface) { texture = SDL_CreateTextureFromSurface(renderer, surface); SDL_FreeSurface(surface); } return texture; } SDL_Texture *IMG_LoadTexture_RW(SDL_Renderer *renderer, SDL_RWops *src, int freesrc) { SDL_Texture *texture = NULL; SDL_Surface *surface = IMG_Load_RW(src, freesrc); if (surface) { texture = SDL_CreateTextureFromSurface(renderer, surface); SDL_FreeSurface(surface); } return texture; } SDL_Texture *IMG_LoadTextureTyped_RW(SDL_Renderer *renderer, SDL_RWops *src, int freesrc, const char *type) { SDL_Texture *texture = NULL; SDL_Surface *surface = IMG_LoadTyped_RW(src, freesrc, type); if (surface) { texture = SDL_CreateTextureFromSurface(renderer, surface); SDL_FreeSurface(surface); } return texture; } #endif /* SDL 2.0 */ /* Load an animation from a file */ IMG_Animation *IMG_LoadAnimation(const char *file) { SDL_RWops *src = SDL_RWFromFile(file, "rb"); const char *ext = SDL_strrchr(file, '.'); if (ext) { ext++; } if (!src) { /* The error message has been set in SDL_RWFromFile */ return NULL; } return IMG_LoadAnimationTyped_RW(src, 1, ext); } /* Load an animation from an SDL datasource (for compatibility) */ IMG_Animation *IMG_LoadAnimation_RW(SDL_RWops *src, int freesrc) { return IMG_LoadAnimationTyped_RW(src, freesrc, NULL); } /* Load an animation from an SDL datasource, optionally specifying the type */ IMG_Animation *IMG_LoadAnimationTyped_RW(SDL_RWops *src, int freesrc, const char *type) { int i; IMG_Animation *anim; SDL_Surface *image; /* Make sure there is something to do.. */ if ( src == NULL ) { IMG_SetError("Passed a NULL data source"); return(NULL); } /* See whether or not this data source can handle seeking */ if ( SDL_RWseek(src, 0, RW_SEEK_CUR) < 0 ) { IMG_SetError("Can't seek in this data source"); if (freesrc) SDL_RWclose(src); return(NULL); } /* Detect the type of image being loaded */ for ( i=0; i < SDL_arraysize(supported_anims); ++i ) { if (supported_anims[i].is) { if (!supported_anims[i].is(src)) continue; } else { /* magicless format */ if (!type || !IMG_string_equals(type, supported_anims[i].type)) continue; } #ifdef DEBUG_IMGLIB fprintf(stderr, "IMGLIB: Loading image as %s\n", supported_anims[i].type); #endif anim = supported_anims[i].load(src); if (freesrc) SDL_RWclose(src); return anim; } /* Create a single frame animation from an image */ image = IMG_LoadTyped_RW(src, freesrc, type); if (image) { anim = (IMG_Animation *)SDL_malloc(sizeof(*anim)); if (anim) { anim->w = image->w; anim->h = image->h; anim->count = 1; anim->frames = (SDL_Surface **)SDL_calloc(anim->count, sizeof(*anim->frames)); anim->delays = (int *)SDL_calloc(anim->count, sizeof(*anim->delays)); if (anim->frames && anim->delays) { anim->frames[0] = image; return anim; } IMG_FreeAnimation(anim); } SDL_FreeSurface(image); SDL_OutOfMemory(); } return NULL; } void IMG_FreeAnimation(IMG_Animation *anim) { if (anim) { if (anim->frames) { int i; for (i = 0; i < anim->count; ++i) { if (anim->frames[i]) { SDL_FreeSurface(anim->frames[i]); } } SDL_free(anim->frames); } if (anim->delays) { SDL_free(anim->delays); } SDL_free(anim); } } SDL2_image-2.8.8/src/IMG_WIC.c0000664000000000000000000001730314751445211012410 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #if defined(SDL_IMAGE_USE_WIC_BACKEND) #include "SDL_image.h" #define COBJMACROS #include #include static IWICImagingFactory* wicFactory = NULL; static int WIC_Init() { if (wicFactory == NULL) { HRESULT hr = CoCreateInstance( &CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, &IID_IWICImagingFactory, (void**)&wicFactory ); if (FAILED(hr)) { return -1; } } return 0; } static void WIC_Quit() { if (wicFactory) { IWICImagingFactory_Release(wicFactory); } } int IMG_InitPNG() { return WIC_Init(); } void IMG_QuitPNG() { WIC_Quit(); } int IMG_InitJPG() { return WIC_Init(); } void IMG_QuitJPG() { WIC_Quit(); } int IMG_InitTIF() { return WIC_Init(); } void IMG_QuitTIF() { WIC_Quit(); } int IMG_isPNG(SDL_RWops *src) { Sint64 start; int is_PNG; Uint8 magic[4]; if ( !src ) { return 0; } start = SDL_RWtell(src); is_PNG = 0; if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) { if ( magic[0] == 0x89 && magic[1] == 'P' && magic[2] == 'N' && magic[3] == 'G' ) { is_PNG = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_PNG); } int IMG_isJPG(SDL_RWops *src) { Sint64 start; int is_JPG; int in_scan; Uint8 magic[4]; /* This detection code is by Steaphan Greene */ /* Blame me, not Sam, if this doesn't work right. */ /* And don't forget to report the problem to the the sdl list too! */ if (!src) return 0; start = SDL_RWtell(src); is_JPG = 0; in_scan = 0; if (SDL_RWread(src, magic, 2, 1)) { if ((magic[0] == 0xFF) && (magic[1] == 0xD8)) { is_JPG = 1; while (is_JPG == 1) { if (SDL_RWread(src, magic, 1, 2) != 2) { is_JPG = 0; } else if ((magic[0] != 0xFF) && (in_scan == 0)) { is_JPG = 0; } else if ((magic[0] != 0xFF) || (magic[1] == 0xFF)) { /* Extra padding in JPEG (legal) */ /* or this is data and we are scanning */ SDL_RWseek(src, -1, RW_SEEK_CUR); } else if (magic[1] == 0xD9) { /* Got to end of good JPEG */ break; } else if ((in_scan == 1) && (magic[1] == 0x00)) { /* This is an encoded 0xFF within the data */ } else if ((magic[1] >= 0xD0) && (magic[1] < 0xD9)) { /* These have nothing else */ } else if (SDL_RWread(src, magic + 2, 1, 2) != 2) { is_JPG = 0; } else { /* Yes, it's big-endian */ Sint64 innerStart; Uint32 size; Sint64 end; innerStart = SDL_RWtell(src); size = (magic[2] << 8) + magic[3]; end = SDL_RWseek(src, size - 2, RW_SEEK_CUR); if (end != innerStart + size - 2) is_JPG = 0; if (magic[1] == 0xDA) { /* Now comes the actual JPEG meat */ #ifdef FAST_IS_JPEG /* Ok, I'm convinced. It is a JPEG. */ break; #else /* I'm not convinced. Prove it! */ in_scan = 1; #endif } } } } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_JPG); } int IMG_isTIF(SDL_RWops* src) { Sint64 start; int is_TIF; Uint8 magic[4]; if (!src) return 0; start = SDL_RWtell(src); is_TIF = 0; if (SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic)) { if ((magic[0] == 'I' && magic[1] == 'I' && magic[2] == 0x2a && magic[3] == 0x00) || (magic[0] == 'M' && magic[1] == 'M' && magic[2] == 0x00 && magic[3] == 0x2a)) { is_TIF = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_TIF); } static SDL_Surface* WIC_LoadImage(SDL_RWops *src) { SDL_Surface* surface = NULL; IWICStream* stream = NULL; IWICBitmapDecoder* bitmapDecoder = NULL; IWICBitmapFrameDecode* bitmapFrame = NULL; IWICFormatConverter* formatConverter = NULL; UINT width, height; if (wicFactory == NULL && (WIC_Init() < 0)) { IMG_SetError("WIC failed to initialize!"); return NULL; } Sint64 fileSize = SDL_RWsize(src); Uint8* memoryBuffer = (Uint8*)SDL_malloc(fileSize); if (!memoryBuffer) { SDL_OutOfMemory(); return NULL; } SDL_RWread(src, memoryBuffer, 1, fileSize); #define DONE_IF_FAILED(X) if (FAILED((X))) { goto done; } DONE_IF_FAILED(IWICImagingFactory_CreateStream(wicFactory, &stream)); DONE_IF_FAILED(IWICStream_InitializeFromMemory(stream, memoryBuffer, fileSize)); DONE_IF_FAILED(IWICImagingFactory_CreateDecoderFromStream( wicFactory, (IStream*)stream, NULL, WICDecodeMetadataCacheOnDemand, &bitmapDecoder )); DONE_IF_FAILED(IWICBitmapDecoder_GetFrame(bitmapDecoder, 0, &bitmapFrame)); DONE_IF_FAILED(IWICImagingFactory_CreateFormatConverter(wicFactory, &formatConverter)); DONE_IF_FAILED(IWICFormatConverter_Initialize( formatConverter, (IWICBitmapSource*)bitmapFrame, &GUID_WICPixelFormat32bppPRGBA, WICBitmapDitherTypeNone, NULL, 0.0, WICBitmapPaletteTypeCustom )); DONE_IF_FAILED(IWICBitmapFrameDecode_GetSize(bitmapFrame, &width, &height)); #undef DONE_IF_FAILED surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 0, SDL_PIXELFORMAT_ABGR8888); IWICFormatConverter_CopyPixels( formatConverter, NULL, width * 4, width * height * 4, (BYTE*)surface->pixels ); done: if (formatConverter) { IWICFormatConverter_Release(formatConverter); } if (bitmapFrame) { IWICBitmapFrameDecode_Release(bitmapFrame); } if (bitmapDecoder) { IWICBitmapDecoder_Release(bitmapDecoder); } if (stream) { IWICStream_Release(stream); } SDL_free(memoryBuffer); return surface; } SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src) { return WIC_LoadImage(src); } SDL_Surface *IMG_LoadJPG_RW(SDL_RWops *src) { return WIC_LoadImage(src); } SDL_Surface *IMG_LoadTIF_RW(SDL_RWops *src) { return WIC_LoadImage(src); } #endif /* SDL_IMAGE_USE_WIC_BACKEND */ SDL2_image-2.8.8/src/IMG_avif.c0000664000000000000000000002243514751445211012715 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This is a AVIF image file loading framework */ #include "SDL_image.h" #ifdef LOAD_AVIF #include #include /* for INT_MAX */ static struct { int loaded; void *handle; avifBool (*avifPeekCompatibleFileType)(const avifROData * input); avifDecoder * (*avifDecoderCreate)(void); void (*avifDecoderDestroy)(avifDecoder * decoder); void (*avifDecoderSetIO)(avifDecoder * decoder, avifIO * io); avifResult (*avifDecoderParse)(avifDecoder * decoder); avifResult (*avifDecoderNextImage)(avifDecoder * decoder); avifResult (*avifImageYUVToRGB)(const avifImage * image, avifRGBImage * rgb); const char * (*avifResultToString)(avifResult res); } lib; #ifdef LOAD_AVIF_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ lib.FUNC = (SIG) SDL_LoadFunction(lib.handle, #FUNC); \ if (lib.FUNC == NULL) { SDL_UnloadObject(lib.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ lib.FUNC = FUNC; \ if (lib.FUNC == NULL) { IMG_SetError("Missing avif.framework"); return -1; } #endif #ifdef __APPLE__ /* Need to turn off optimizations so weak framework load check works */ __attribute__ ((optnone)) #endif int IMG_InitAVIF() { if ( lib.loaded == 0 ) { #ifdef LOAD_AVIF_DYNAMIC lib.handle = SDL_LoadObject(LOAD_AVIF_DYNAMIC); if ( lib.handle == NULL ) { return -1; } #endif FUNCTION_LOADER(avifPeekCompatibleFileType, avifBool (*)(const avifROData * input)) FUNCTION_LOADER(avifDecoderCreate, avifDecoder *(*)(void)) FUNCTION_LOADER(avifDecoderDestroy, void (*)(avifDecoder * decoder)) FUNCTION_LOADER(avifDecoderSetIO, void (*)(avifDecoder * decoder, avifIO * io)) FUNCTION_LOADER(avifDecoderParse, avifResult (*)(avifDecoder * decoder)) FUNCTION_LOADER(avifDecoderNextImage, avifResult (*)(avifDecoder * decoder)) FUNCTION_LOADER(avifImageYUVToRGB, avifResult (*)(const avifImage * image, avifRGBImage * rgb)) FUNCTION_LOADER(avifResultToString, const char *(*)(avifResult res)) } ++lib.loaded; return 0; } void IMG_QuitAVIF() { if ( lib.loaded == 0 ) { return; } if ( lib.loaded == 1 ) { #ifdef LOAD_AVIF_DYNAMIC SDL_UnloadObject(lib.handle); #endif } --lib.loaded; } static SDL_bool ReadAVIFHeader(SDL_RWops *src, Uint8 **header_data, size_t *header_size) { Uint8 magic[16]; Uint64 size; size_t read = 0; Uint8 *data; *header_data = NULL; *header_size = 0; if (!SDL_RWread(src, magic, 8, 1)) { return SDL_FALSE; } read += 8; if (SDL_memcmp(&magic[4], "ftyp", 4) != 0) { return SDL_FALSE; } size = (((size_t)magic[0] << 24) | ((size_t)magic[1] << 16) | ((size_t)magic[2] << 8) | ((size_t)magic[3] << 0)); if (size == 1) { /* 64-bit header size */ if (!SDL_RWread(src, &magic[8], 8, 1)) { return SDL_FALSE; } read += 8; size = (((Uint64)magic[8] << 56) | ((Uint64)magic[9] << 48) | ((Uint64)magic[10] << 40) | ((Uint64)magic[11] << 32) | ((Uint64)magic[12] << 24) | ((Uint64)magic[13] << 16) | ((Uint64)magic[14] << 8) | ((Uint64)magic[15] << 0)); } if (size > INT_MAX) { return SDL_FALSE; } if (size <= read) { return SDL_FALSE; } /* Read in the header */ data = (Uint8 *)SDL_malloc((size_t)size); if (!data) { return SDL_FALSE; } SDL_memcpy(data, magic, read); if (!SDL_RWread(src, &data[read], (size - read), 1)) { SDL_free(data); return SDL_FALSE; } *header_data = data; *header_size = (size_t)size; return SDL_TRUE; } /* See if an image is contained in a data source */ int IMG_isAVIF(SDL_RWops *src) { Sint64 start; int is_AVIF; Uint8 *data; size_t size; if ( !src ) return 0; start = SDL_RWtell(src); is_AVIF = 0; if (ReadAVIFHeader(src, &data, &size)) { /* This might be AVIF, do more thorough checks */ if ((IMG_Init(IMG_INIT_AVIF) & IMG_INIT_AVIF) != 0) { avifROData header; header.data = data; header.size = size; is_AVIF = lib.avifPeekCompatibleFileType(&header); } SDL_free(data); } SDL_RWseek(src, start, RW_SEEK_SET); return(is_AVIF); } /* Context for AFIF I/O operations */ typedef struct { SDL_RWops *src; Uint64 start; uint8_t *data; size_t size; } avifIOContext; static avifResult ReadAVIFIO(struct avifIO * io, uint32_t readFlags, uint64_t offset, size_t size, avifROData * out) { avifIOContext *context = (avifIOContext *)io->data; (void) readFlags; /* not used */ /* The AVIF reader bounces all over, so always seek to the correct offset */ if (SDL_RWseek(context->src, context->start + offset, RW_SEEK_SET) < 0) { return AVIF_RESULT_IO_ERROR; } if (size > (Uint64)context->size) { uint8_t *data = (uint8_t *)SDL_realloc(context->data, size); if (!data) { return AVIF_RESULT_IO_ERROR; } context->data = data; context->size = (Sint64)size; } out->data = context->data; out->size = SDL_RWread(context->src, context->data, 1, size); if (out->size == 0) { return AVIF_RESULT_IO_ERROR; } return AVIF_RESULT_OK; } static void DestroyAVIFIO(struct avifIO * io) { avifIOContext *context = (avifIOContext *)io->data; if (context->data) { SDL_free(context->data); context->data = NULL; } } /* Load a AVIF type image from an SDL datasource */ SDL_Surface *IMG_LoadAVIF_RW(SDL_RWops *src) { Sint64 start; avifDecoder *decoder = NULL; avifIO io; avifIOContext context; avifRGBImage rgb; avifResult result; SDL_Surface *surface = NULL; if (!src) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); if ((IMG_Init(IMG_INIT_AVIF) & IMG_INIT_AVIF) == 0) { return NULL; } SDL_zero(context); SDL_zero(io); SDL_zero(rgb); decoder = lib.avifDecoderCreate(); if (!decoder) { IMG_SetError("Couldn't create AVIF decoder"); goto done; } /* Be permissive so we can load as many images as possible */ decoder->strictFlags = AVIF_STRICT_DISABLED; context.src = src; context.start = start; io.destroy = DestroyAVIFIO; io.read = ReadAVIFIO; io.data = &context; lib.avifDecoderSetIO(decoder, &io); result = lib.avifDecoderParse(decoder); if (result != AVIF_RESULT_OK) { IMG_SetError("Couldn't parse AVIF image: %s", lib.avifResultToString(result)); goto done; } result = lib.avifDecoderNextImage(decoder); if (result != AVIF_RESULT_OK) { IMG_SetError("Couldn't get AVIF image: %s", lib.avifResultToString(result)); goto done; } surface = SDL_CreateRGBSurfaceWithFormat(0, decoder->image->width, decoder->image->height, 0, SDL_PIXELFORMAT_ARGB8888); if (!surface) { goto done; } /* Convert the YUV image to RGB */ rgb.width = surface->w; rgb.height = surface->h; rgb.depth = 8; #if SDL_BYTEORDER == SDL_LIL_ENDIAN rgb.format = AVIF_RGB_FORMAT_BGRA; #else rgb.format = AVIF_RGB_FORMAT_ARGB; #endif rgb.pixels = (uint8_t *)surface->pixels; rgb.rowBytes = (uint32_t)surface->pitch; result = lib.avifImageYUVToRGB(decoder->image, &rgb); if (result != AVIF_RESULT_OK) { IMG_SetError("Couldn't convert AVIF image to RGB: %s", lib.avifResultToString(result)); SDL_FreeSurface(surface); surface = NULL; goto done; } done: if (decoder) { lib.avifDecoderDestroy(decoder); } if (!surface) { SDL_RWseek(src, start, RW_SEEK_SET); } return surface; } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif int IMG_InitAVIF() { IMG_SetError("AVIF images are not supported"); return(-1); } void IMG_QuitAVIF() { } /* See if an image is contained in a data source */ int IMG_isAVIF(SDL_RWops *src) { return(0); } /* Load a AVIF type image from an SDL datasource */ SDL_Surface *IMG_LoadAVIF_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_AVIF */ SDL2_image-2.8.8/src/IMG_bmp.c0000664000000000000000000003272114761417066012555 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #if (!defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND)) || !defined(BMP_USES_IMAGEIO) /* This is a BMP image file loading framework * * ICO/CUR file support is here as well since it uses similar internal * representation * * A good test suite of BMP images is available at: * http://entropymine.com/jason/bmpsuite/bmpsuite/html/bmpsuite.html */ #include "SDL_image.h" #ifdef LOAD_BMP /* See if an image is contained in a data source */ int IMG_isBMP(SDL_RWops *src) { Sint64 start; int is_BMP; char magic[2]; if ( !src ) return 0; start = SDL_RWtell(src); is_BMP = 0; if ( SDL_RWread(src, magic, sizeof(magic), 1) ) { if ( SDL_strncmp(magic, "BM", 2) == 0 ) { is_BMP = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_BMP); } static int IMG_isICOCUR(SDL_RWops *src, int type) { Sint64 start; int is_ICOCUR; /* The Win32 ICO file header (14 bytes) */ Uint16 bfReserved; Uint16 bfType; Uint16 bfCount; if ( !src ) return 0; start = SDL_RWtell(src); is_ICOCUR = 0; bfReserved = SDL_ReadLE16(src); bfType = SDL_ReadLE16(src); bfCount = SDL_ReadLE16(src); if ((bfReserved == 0) && (bfType == type) && (bfCount != 0)) is_ICOCUR = 1; SDL_RWseek(src, start, RW_SEEK_SET); return (is_ICOCUR); } int IMG_isICO(SDL_RWops *src) { return IMG_isICOCUR(src, 1); } int IMG_isCUR(SDL_RWops *src) { return IMG_isICOCUR(src, 2); } #include "SDL_error.h" #include "SDL_video.h" #include "SDL_endian.h" /* Compression encodings for BMP files */ #ifndef BI_RGB #define BI_RGB 0 #define BI_RLE8 1 #define BI_RLE4 2 #define BI_BITFIELDS 3 #endif static SDL_Surface *LoadBMP_RW (SDL_RWops *src, int freesrc) { return SDL_LoadBMP_RW(src, freesrc); } static Uint8 SDL_Read8(SDL_RWops * src) { Uint8 value; SDL_RWread(src, &value, 1, 1); return (value); } static SDL_Surface * LoadICOCUR_RW(SDL_RWops * src, int type, int freesrc) { SDL_bool was_error; Sint64 fp_offset = 0; int bmpPitch; int i,j, pad; SDL_Surface *surface; /* Uint32 Rmask; Uint32 Gmask; Uint32 Bmask; */ Uint8 *bits; int ExpandBMP; int maxCol = 0; int icoOfs = 0; Uint32 palette[256]; /* The Win32 ICO file header (14 bytes) */ Uint16 bfReserved; Uint16 bfType; Uint16 bfCount; /* The Win32 BITMAPINFOHEADER struct (40 bytes) */ Uint32 biSize; Sint32 biWidth; Sint32 biHeight; /* Uint16 biPlanes; */ Uint16 biBitCount; Uint32 biCompression; /* Uint32 biSizeImage; Sint32 biXPelsPerMeter; Sint32 biYPelsPerMeter; Uint32 biClrImportant; */ Uint32 biClrUsed; /* Make sure we are passed a valid data source */ surface = NULL; was_error = SDL_FALSE; if (src == NULL) { was_error = SDL_TRUE; goto done; } /* Read in the ICO file header */ fp_offset = SDL_RWtell(src); SDL_ClearError(); bfReserved = SDL_ReadLE16(src); bfType = SDL_ReadLE16(src); bfCount = SDL_ReadLE16(src); if ((bfReserved != 0) || (bfType != type) || (bfCount == 0)) { IMG_SetError("File is not a Windows %s file", type == 1 ? "ICO" : "CUR"); was_error = SDL_TRUE; goto done; } /* Read the Win32 Icon Directory */ for (i = 0; i < bfCount; i++) { /* Icon Directory Entries */ int bWidth = SDL_Read8(src); /* Uint8, but 0 = 256 ! */ int bHeight = SDL_Read8(src); /* Uint8, but 0 = 256 ! */ int bColorCount = SDL_Read8(src); /* Uint8, but 0 = 256 ! */ /* Uint8 bReserved; Uint16 wPlanes; Uint16 wBitCount; Uint32 dwBytesInRes; */ Uint32 dwImageOffset; /* bReserved = */ SDL_Read8(src); /* wPlanes = */ SDL_ReadLE16(src); /* wBitCount = */ SDL_ReadLE16(src); /* dwBytesInRes = */ SDL_ReadLE32(src); dwImageOffset = SDL_ReadLE32(src); if (!bWidth) bWidth = 256; if (!bHeight) bHeight = 256; if (!bColorCount) bColorCount = 256; //printf("%dx%d@%d - %08x\n", bWidth, bHeight, bColorCount, dwImageOffset); if (bColorCount > maxCol) { maxCol = bColorCount; icoOfs = dwImageOffset; //printf("marked\n"); } } /* Advance to the DIB Data */ if (SDL_RWseek(src, icoOfs, RW_SEEK_SET) < 0) { SDL_Error(SDL_EFSEEK); was_error = SDL_TRUE; goto done; } /* Read the Win32 BITMAPINFOHEADER */ biSize = SDL_ReadLE32(src); if (biSize == 40) { biWidth = SDL_ReadLE32(src); biHeight = SDL_ReadLE32(src); /* biPlanes = */ SDL_ReadLE16(src); biBitCount = SDL_ReadLE16(src); biCompression = SDL_ReadLE32(src); /* biSizeImage = */ SDL_ReadLE32(src); /* biXPelsPerMeter = */ SDL_ReadLE32(src); /* biYPelsPerMeter = */ SDL_ReadLE32(src); biClrUsed = SDL_ReadLE32(src); /* biClrImportant = */ SDL_ReadLE32(src); } else { IMG_SetError("Unsupported ICO bitmap format"); was_error = SDL_TRUE; goto done; } /* Check for read error */ if (SDL_strcmp(SDL_GetError(), "") != 0) { was_error = SDL_TRUE; goto done; } /* We don't support any BMP compression right now */ switch (biCompression) { case BI_RGB: /* Default values for the BMP format */ switch (biBitCount) { case 1: case 4: ExpandBMP = biBitCount; break; case 8: ExpandBMP = 8; break; case 24: ExpandBMP = 24; break; case 32: /* Rmask = 0x00FF0000; Gmask = 0x0000FF00; Bmask = 0x000000FF; */ ExpandBMP = 0; break; default: IMG_SetError("ICO file with unsupported bit count"); was_error = SDL_TRUE; goto done; } break; default: IMG_SetError("Compressed ICO files not supported"); was_error = SDL_TRUE; goto done; } /* sanity check image size, so we don't overflow integers, etc. */ if ((biWidth < 0) || (biWidth > 0xFFFFFF) || (biHeight < 0) || (biHeight > 0xFFFFFF)) { IMG_SetError("Unsupported or invalid ICO dimensions"); was_error = SDL_TRUE; goto done; } /* Create a RGBA surface */ biHeight = biHeight >> 1; //printf("%d x %d\n", biWidth, biHeight); surface = SDL_CreateRGBSurfaceWithFormat(0, biWidth, biHeight, 0, SDL_PIXELFORMAT_ARGB8888); if (surface == NULL) { was_error = SDL_TRUE; goto done; } /* Load the palette, if any */ //printf("bc %d bused %d\n", biBitCount, biClrUsed); if (biBitCount <= 8) { if (biClrUsed == 0) { biClrUsed = 1 << biBitCount; } if (biClrUsed > SDL_arraysize(palette)) { IMG_SetError("Unsupported or incorrect biClrUsed field"); was_error = SDL_TRUE; goto done; } for (i = 0; i < (int) biClrUsed; ++i) { SDL_RWread(src, &palette[i], 4, 1); /* Since biSize == 40, we know alpha is reserved and should be zero, meaning opaque */ if ((palette[i] & 0xFF000000) == 0) { palette[i] |= 0xFF000000; } } } /* Read the surface pixels. Note that the bmp image is upside down */ bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch); switch (ExpandBMP) { case 1: bmpPitch = (biWidth + 7) >> 3; pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0); break; case 4: bmpPitch = (biWidth + 1) >> 1; pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0); break; case 8: bmpPitch = biWidth; pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0); break; case 24: bmpPitch = biWidth * 3; pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0); break; default: bmpPitch = biWidth * 4; pad = 0; break; } while (bits > (Uint8 *) surface->pixels) { bits -= surface->pitch; switch (ExpandBMP) { case 1: case 4: case 8: { Uint8 pixel = 0; int shift = (8 - ExpandBMP); for (i = 0; i < surface->w; ++i) { if (i % (8 / ExpandBMP) == 0) { if (!SDL_RWread(src, &pixel, 1, 1)) { IMG_SetError("Error reading from ICO"); was_error = SDL_TRUE; goto done; } } *((Uint32 *) bits + i) = (palette[pixel >> shift]); pixel <<= ExpandBMP; } } break; case 24: { Uint32 pixel; Uint8 channel; for (i = 0; i < surface->w; ++i) { pixel = 0xFF000000; for (j = 0; j < 3; ++j) { /* Load each color channel into pixel */ if (!SDL_RWread(src, &channel, 1, 1)) { IMG_SetError("Error reading from ICO"); was_error = SDL_TRUE; goto done; } pixel |= (channel << (j * 8)); } *((Uint32 *) bits + i) = pixel; } } break; default: if (SDL_RWread(src, bits, 1, surface->pitch) != surface->pitch) { SDL_Error(SDL_EFREAD); was_error = SDL_TRUE; goto done; } break; } /* Skip padding bytes, ugh */ if (pad) { Uint8 padbyte; for (i = 0; i < pad; ++i) { SDL_RWread(src, &padbyte, 1, 1); } } } /* Read the mask pixels. Note that the bmp image is upside down */ bits = (Uint8 *) surface->pixels + (surface->h * surface->pitch); ExpandBMP = 1; bmpPitch = (biWidth + 7) >> 3; pad = (((bmpPitch) % 4) ? (4 - ((bmpPitch) % 4)) : 0); while (bits > (Uint8 *) surface->pixels) { Uint8 pixel = 0; int shift = (8 - ExpandBMP); bits -= surface->pitch; for (i = 0; i < surface->w; ++i) { if (i % (8 / ExpandBMP) == 0) { if (!SDL_RWread(src, &pixel, 1, 1)) { IMG_SetError("Error reading from ICO"); was_error = SDL_TRUE; goto done; } } *((Uint32 *) bits + i) &= ((pixel >> shift) ? 0 : 0xFFFFFFFF); pixel <<= ExpandBMP; } /* Skip padding bytes, ugh */ if (pad) { Uint8 padbyte; for (i = 0; i < pad; ++i) { SDL_RWread(src, &padbyte, 1, 1); } } } done: if (was_error) { if (src) { SDL_RWseek(src, fp_offset, RW_SEEK_SET); } if (surface) { SDL_FreeSurface(surface); } surface = NULL; } if (freesrc && src) { SDL_RWclose(src); } return (surface); } /* Load a BMP type image from an SDL datasource */ SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src) { return(LoadBMP_RW(src, 0)); } /* Load a ICO type image from an SDL datasource */ SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src) { return(LoadICOCUR_RW(src, 1, 0)); } /* Load a CUR type image from an SDL datasource */ SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src) { return(LoadICOCUR_RW(src, 2, 0)); } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif /* See if an image is contained in a data source */ int IMG_isBMP(SDL_RWops *src) { return(0); } int IMG_isICO(SDL_RWops *src) { return(0); } int IMG_isCUR(SDL_RWops *src) { return(0); } /* Load a BMP type image from an SDL datasource */ SDL_Surface *IMG_LoadBMP_RW(SDL_RWops *src) { return(NULL); } /* Load a BMP type image from an SDL datasource */ SDL_Surface *IMG_LoadCUR_RW(SDL_RWops *src) { return(NULL); } /* Load a BMP type image from an SDL datasource */ SDL_Surface *IMG_LoadICO_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_BMP */ #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */ SDL2_image-2.8.8/src/IMG_gif.c0000664000000000000000000005602514751445211012537 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This is a GIF image file loading framework */ #include "SDL_image.h" #ifdef LOAD_GIF /* Code from here to end of file has been adapted from XPaint: */ /* +-------------------------------------------------------------------+ */ /* | Copyright 1990, 1991, 1993 David Koblas. | */ /* | Copyright 1996 Torsten Martinsen. | */ /* | Permission to use, copy, modify, and distribute this software | */ /* | and its documentation for any purpose and without fee is hereby | */ /* | granted, provided that the above copyright notice appear in all | */ /* | copies and that both that copyright notice and this permission | */ /* | notice appear in supporting documentation. This software is | */ /* | provided "as is" without express or implied warranty. | */ /* +-------------------------------------------------------------------+ */ /* Adapted for use in SDL by Sam Lantinga -- 7/20/98 */ /* Changes to work with SDL: Include SDL header file Use SDL_Surface rather than xpaint Image structure Define SDL versions of RWSetMsg(), ImageNewCmap() and ImageSetCmap() */ #include "SDL.h" #define Image SDL_Surface #define RWSetMsg IMG_SetError #define ImageNewCmap(w, h, s) SDL_CreateRGBSurfaceWithFormat(0, w, h, 0, SDL_PIXELFORMAT_INDEX8) #define ImageSetCmap(s, i, R, G, B) do { \ s->format->palette->colors[i].r = R; \ s->format->palette->colors[i].g = G; \ s->format->palette->colors[i].b = B; \ } while (0) /* * * * * */ #define GIF_DISPOSE_NA 0 /* No disposal specified */ #define GIF_DISPOSE_NONE 1 /* Do not dispose */ #define GIF_DISPOSE_RESTORE_BACKGROUND 2 /* Restore to background */ #define GIF_DISPOSE_RESTORE_PREVIOUS 3 /* Restore to previous */ #define MAXCOLORMAPSIZE 256 #define TRUE 1 #define FALSE 0 #define CM_RED 0 #define CM_GREEN 1 #define CM_BLUE 2 #define MAX_LWZ_BITS 12 #define INTERLACE 0x40 #define LOCALCOLORMAP 0x80 #define BitSet(byte, bit) (((byte) & (bit)) == (bit)) #define ReadOK(file,buffer,len) SDL_RWread(file, buffer, len, 1) #define LM_to_uint(a,b) (((b)<<8)|(a)) typedef struct { struct { unsigned int Width; unsigned int Height; unsigned char ColorMap[3][MAXCOLORMAPSIZE]; unsigned int BitPixel; unsigned int ColorResolution; unsigned int Background; unsigned int AspectRatio; int GrayScale; } GifScreen; struct { int transparent; int delayTime; int inputFlag; int disposal; } Gif89; unsigned char buf[280]; int curbit, lastbit, done, last_byte; int fresh; int code_size, set_code_size; int max_code, max_code_size; int firstcode, oldcode; int clear_code, end_code; int table[2][(1 << MAX_LWZ_BITS)]; int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp; int ZeroDataBlock; } State_t; typedef struct { Image *image; int x, y; int disposal; int delay; } Frame_t; typedef struct { int count; Frame_t *frames; } Anim_t; static int ReadColorMap(SDL_RWops * src, int number, unsigned char buffer[3][MAXCOLORMAPSIZE], int *flag); static int DoExtension(SDL_RWops * src, int label, State_t * state); static int GetDataBlock(SDL_RWops * src, unsigned char *buf, State_t * state); static int GetCode(SDL_RWops * src, int code_size, int flag, State_t * state); static int LWZReadByte(SDL_RWops * src, int flag, int input_code_size, State_t * state); static Image *ReadImage(SDL_RWops * src, int len, int height, int, unsigned char cmap[3][MAXCOLORMAPSIZE], int gray, int interlace, int ignore, State_t * state); static SDL_bool NormalizeFrames(Frame_t *frames, int count) { SDL_Surface *image; int i; int lastDispose = GIF_DISPOSE_RESTORE_BACKGROUND; int iRestore = 0; Uint32 fill; SDL_Rect rect; if (SDL_HasColorKey(frames[0].image)) { image = SDL_ConvertSurfaceFormat(frames[0].image, SDL_PIXELFORMAT_ARGB8888, 0); } else { image = SDL_ConvertSurfaceFormat(frames[0].image, SDL_PIXELFORMAT_RGB888, 0); } if (!image) { return SDL_FALSE; } fill = SDL_MapRGBA(image->format, 0, 0, 0, SDL_ALPHA_TRANSPARENT); rect.x = 0; rect.y = 0; rect.w = image->w; rect.h = image->h; for (i = 0; i < count; ++i) { switch (lastDispose) { case GIF_DISPOSE_RESTORE_BACKGROUND: SDL_FillRect(image, &rect, fill); break; case GIF_DISPOSE_RESTORE_PREVIOUS: SDL_BlitSurface(frames[iRestore].image, &rect, image, &rect); break; default: break; } if (frames[i].disposal != GIF_DISPOSE_RESTORE_PREVIOUS) { iRestore = i; } rect.x = (Sint16)frames[i].x; rect.y = (Sint16)frames[i].y; rect.w = frames[i].image->w; rect.h = frames[i].image->h; SDL_BlitSurface(frames[i].image, NULL, image, &rect); SDL_FreeSurface(frames[i].image); frames[i].image = SDL_DuplicateSurface(image); if (!frames[i].image) { return SDL_FALSE; } lastDispose = frames[i].disposal; } SDL_FreeSurface( image ); return SDL_TRUE; } static Anim_t * IMG_LoadGIF_RW_Internal(SDL_RWops *src, SDL_bool load_anim) { unsigned char buf[16]; unsigned char c; unsigned char localColorMap[3][MAXCOLORMAPSIZE]; int grayScale; int useGlobalColormap; int bitPixel; char version[4]; State_t *state = NULL; Image *image = NULL; Anim_t *anim; Frame_t *frames, *frame; if (src == NULL) { return NULL; } anim = (Anim_t *)SDL_calloc(1, sizeof(*anim)); if (!anim) { SDL_OutOfMemory(); return NULL; } if (!ReadOK(src, buf, 6)) { RWSetMsg("error reading magic number"); goto done; } if (SDL_strncmp((char *) buf, "GIF", 3) != 0) { RWSetMsg("not a GIF file"); goto done; } SDL_memcpy(version, (char *) buf + 3, 3); version[3] = '\0'; if ((SDL_strcmp(version, "87a") != 0) && (SDL_strcmp(version, "89a") != 0)) { RWSetMsg("bad version number, not '87a' or '89a'"); goto done; } state = (State_t *)SDL_calloc(1, sizeof(State_t)); if (state == NULL) { SDL_OutOfMemory(); goto done; } state->Gif89.transparent = -1; state->Gif89.delayTime = -1; state->Gif89.inputFlag = -1; state->Gif89.disposal = GIF_DISPOSE_NA; if (!ReadOK(src, buf, 7)) { RWSetMsg("failed to read screen descriptor"); goto done; } state->GifScreen.Width = LM_to_uint(buf[0], buf[1]); state->GifScreen.Height = LM_to_uint(buf[2], buf[3]); state->GifScreen.BitPixel = 2 << (buf[4] & 0x07); state->GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1); state->GifScreen.Background = buf[5]; state->GifScreen.AspectRatio = buf[6]; if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */ if (ReadColorMap(src, state->GifScreen.BitPixel, state->GifScreen.ColorMap, &state->GifScreen.GrayScale)) { RWSetMsg("error reading global colormap"); goto done; } } for ( ; ; ) { if (!ReadOK(src, &c, 1)) { RWSetMsg("EOF / read error on image data"); goto done; } if (c == ';') { /* GIF terminator */ goto done; } if (c == '!') { /* Extension */ if (!ReadOK(src, &c, 1)) { RWSetMsg("EOF / read error on extension function code"); goto done; } DoExtension(src, c, state); continue; } if (c != ',') { /* Not a valid start character */ continue; } if (!ReadOK(src, buf, 9)) { RWSetMsg("couldn't read left/top/width/height"); goto done; } useGlobalColormap = !BitSet(buf[8], LOCALCOLORMAP); bitPixel = 1 << ((buf[8] & 0x07) + 1); if (!useGlobalColormap) { if (ReadColorMap(src, bitPixel, localColorMap, &grayScale)) { RWSetMsg("error reading local colormap"); goto done; } image = ReadImage(src, LM_to_uint(buf[4], buf[5]), LM_to_uint(buf[6], buf[7]), bitPixel, localColorMap, grayScale, BitSet(buf[8], INTERLACE), 0, state); } else { image = ReadImage(src, LM_to_uint(buf[4], buf[5]), LM_to_uint(buf[6], buf[7]), state->GifScreen.BitPixel, state->GifScreen.ColorMap, state->GifScreen.GrayScale, BitSet(buf[8], INTERLACE), 0, state); } if (image) { if (state->Gif89.transparent >= 0) { SDL_SetColorKey(image, SDL_TRUE, state->Gif89.transparent); } frames = (Frame_t *)SDL_realloc(anim->frames, (anim->count + 1) * sizeof(*anim->frames)); if (!frames) { SDL_OutOfMemory(); goto done; } ++anim->count; anim->frames = frames; frame = &anim->frames[anim->count - 1]; frame->image = image; frame->x = LM_to_uint(buf[0], buf[1]); frame->y = LM_to_uint(buf[2], buf[3]); frame->disposal = state->Gif89.disposal; if (state->Gif89.delayTime < 2) { frame->delay = 100; /* Default animation delay, matching browsers and Qt */ } else { frame->delay = state->Gif89.delayTime * 10; } if (!load_anim) { /* We only need one frame, we're done */ goto done; } } } done: if (anim->count > 1) { /* Normalize the frames */ if (!NormalizeFrames(anim->frames, anim->count)) { int i; for (i = 0; i < anim->count; ++i) { SDL_FreeSurface(anim->frames[i].image); } anim->count = 0; } } if (anim->count == 0) { SDL_free(anim->frames); SDL_free(anim); anim = NULL; } SDL_free(state); return anim; } static int ReadColorMap(SDL_RWops *src, int number, unsigned char buffer[3][MAXCOLORMAPSIZE], int *gray) { int i; unsigned char rgb[3]; int flag; flag = 1; for (i = 0; i < number; ++i) { if (!ReadOK(src, rgb, sizeof(rgb))) { RWSetMsg("bad colormap"); return 1; } buffer[CM_RED][i] = rgb[0]; buffer[CM_GREEN][i] = rgb[1]; buffer[CM_BLUE][i] = rgb[2]; flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]); } #if 0 if (flag) *gray = (number == 2) ? PBM_TYPE : PGM_TYPE; else *gray = PPM_TYPE; #else (void) flag; *gray = 0; #endif return FALSE; } static int DoExtension(SDL_RWops *src, int label, State_t * state) { unsigned char buf[256]; switch (label) { case 0x01: /* Plain Text Extension */ break; case 0xff: /* Application Extension */ break; case 0xfe: /* Comment Extension */ while (GetDataBlock(src, buf, state) > 0) ; return FALSE; case 0xf9: /* Graphic Control Extension */ (void) GetDataBlock(src, buf, state); state->Gif89.disposal = (buf[0] >> 2) & 0x7; state->Gif89.inputFlag = (buf[0] >> 1) & 0x1; state->Gif89.delayTime = LM_to_uint(buf[1], buf[2]); if ((buf[0] & 0x1) != 0) state->Gif89.transparent = buf[3]; while (GetDataBlock(src, buf, state) > 0) ; return FALSE; default: break; } while (GetDataBlock(src, buf, state) > 0) ; return FALSE; } static int GetDataBlock(SDL_RWops *src, unsigned char *buf, State_t * state) { unsigned char count; if (!ReadOK(src, &count, 1)) { /* pm_message("error in getting DataBlock size" ); */ return -1; } state->ZeroDataBlock = count == 0; if ((count != 0) && (!ReadOK(src, buf, count))) { /* pm_message("error in reading DataBlock" ); */ return -1; } return count; } static int GetCode(SDL_RWops *src, int code_size, int flag, State_t * state) { int i, j, ret; unsigned char count; if (flag) { state->curbit = 0; state->lastbit = 0; state->done = FALSE; return 0; } if ((state->curbit + code_size) >= state->lastbit) { if (state->done) { if (state->curbit >= state->lastbit) RWSetMsg("ran off the end of my bits"); return -1; } state->buf[0] = state->buf[state->last_byte - 2]; state->buf[1] = state->buf[state->last_byte - 1]; if ((ret = GetDataBlock(src, &state->buf[2], state)) > 0) count = (unsigned char) ret; else { count = 0; state->done = TRUE; } state->last_byte = 2 + count; state->curbit = (state->curbit - state->lastbit) + 16; state->lastbit = (2 + count) * 8; } ret = 0; for (i = state->curbit, j = 0; j < code_size; ++i, ++j) ret |= ((state->buf[i / 8] & (1 << (i % 8))) != 0) << j; state->curbit += code_size; return ret; } static int LWZReadByte(SDL_RWops *src, int flag, int input_code_size, State_t * state) { int i, code, incode; /* Fixed buffer overflow found by Michael Skladnikiewicz */ if (input_code_size > MAX_LWZ_BITS) return -1; if (flag) { state->set_code_size = input_code_size; state->code_size = state->set_code_size + 1; state->clear_code = 1 << state->set_code_size; state->end_code = state->clear_code + 1; state->max_code_size = 2 * state->clear_code; state->max_code = state->clear_code + 2; GetCode(src, 0, TRUE, state); state->fresh = TRUE; for (i = 0; i < state->clear_code; ++i) { state->table[0][i] = 0; state->table[1][i] = i; } state->table[1][0] = 0; for (; i < (1 << MAX_LWZ_BITS); ++i) state->table[0][i] = 0; state->sp = state->stack; return 0; } else if (state->fresh) { state->fresh = FALSE; do { state->firstcode = state->oldcode = GetCode(src, state->code_size, FALSE, state); } while (state->firstcode == state->clear_code); return state->firstcode; } if (state->sp > state->stack) return *--state->sp; while ((code = GetCode(src, state->code_size, FALSE, state)) >= 0) { if (code == state->clear_code) { for (i = 0; i < state->clear_code; ++i) { state->table[0][i] = 0; state->table[1][i] = i; } for (; i < (1 << MAX_LWZ_BITS); ++i) state->table[0][i] = state->table[1][i] = 0; state->code_size = state->set_code_size + 1; state->max_code_size = 2 * state->clear_code; state->max_code = state->clear_code + 2; state->sp = state->stack; state->firstcode = state->oldcode = GetCode(src, state->code_size, FALSE, state); return state->firstcode; } else if (code == state->end_code) { int count; unsigned char buf[260]; if (state->ZeroDataBlock) return -2; while ((count = GetDataBlock(src, buf, state)) > 0) ; if (count != 0) { /* * pm_message("missing EOD in data stream (common occurrence)"); */ } return -2; } incode = code; if (code >= state->max_code) { *state->sp++ = state->firstcode; code = state->oldcode; } while (code >= state->clear_code) { /* Guard against buffer overruns */ if (code < 0 || code >= (1 << MAX_LWZ_BITS)) { RWSetMsg("invalid LWZ data"); return -3; } *state->sp++ = state->table[1][code]; if (code == state->table[0][code]) { RWSetMsg("circular table entry BIG ERROR"); return -3; } code = state->table[0][code]; } /* Guard against buffer overruns */ if (code < 0 || code >= (1 << MAX_LWZ_BITS)) { RWSetMsg("invalid LWZ data"); return -4; } *state->sp++ = state->firstcode = state->table[1][code]; if ((code = state->max_code) < (1 << MAX_LWZ_BITS)) { state->table[0][code] = state->oldcode; state->table[1][code] = state->firstcode; ++state->max_code; if ((state->max_code >= state->max_code_size) && (state->max_code_size < (1 << MAX_LWZ_BITS))) { state->max_code_size *= 2; ++state->code_size; } } state->oldcode = incode; if (state->sp > state->stack) return *--state->sp; } return code; } static Image * ReadImage(SDL_RWops * src, int len, int height, int cmapSize, unsigned char cmap[3][MAXCOLORMAPSIZE], int gray, int interlace, int ignore, State_t * state) { Image *image; unsigned char c; int i, v; int xpos = 0, ypos = 0, pass = 0; (void) gray; /* unused */ /* ** Initialize the compression routines */ if (!ReadOK(src, &c, 1)) { RWSetMsg("EOF / read error on image data"); return NULL; } if (LWZReadByte(src, TRUE, c, state) < 0) { RWSetMsg("error reading image"); return NULL; } /* ** If this is an "uninteresting picture" ignore it. */ if (ignore) { while (LWZReadByte(src, FALSE, c, state) >= 0) ; return NULL; } image = ImageNewCmap(len, height, cmapSize); if (!image) { return NULL; } if (!image->pixels) { SDL_FreeSurface(image); return NULL; } for (i = 0; i < cmapSize; i++) ImageSetCmap(image, i, cmap[CM_RED][i], cmap[CM_GREEN][i], cmap[CM_BLUE][i]); while ((v = LWZReadByte(src, FALSE, c, state)) >= 0) { ((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = (Uint8)v; ++xpos; if (xpos == len) { xpos = 0; if (interlace) { switch (pass) { case 0: case 1: ypos += 8; break; case 2: ypos += 4; break; case 3: ypos += 2; break; } if (ypos >= height) { ++pass; switch (pass) { case 1: ypos = 4; break; case 2: ypos = 2; break; case 3: ypos = 1; break; default: goto fini; } } } else { ++ypos; } } if (ypos >= height) break; } fini: return image; } /* Load a GIF type animation from an SDL datasource */ IMG_Animation *IMG_LoadGIFAnimation_RW(SDL_RWops *src) { Anim_t *internal = IMG_LoadGIF_RW_Internal(src, SDL_TRUE); if (internal) { IMG_Animation *anim = (IMG_Animation *)SDL_malloc(sizeof(*anim)); if (anim) { anim->w = internal->frames[0].image->w; anim->h = internal->frames[0].image->h; anim->count = internal->count; anim->frames = (SDL_Surface **)SDL_calloc(anim->count, sizeof(*anim->frames)); anim->delays = (int *)SDL_calloc(anim->count, sizeof(*anim->delays)); if (anim->frames && anim->delays) { int i; for (i = 0; i < anim->count; ++i) { anim->frames[i] = internal->frames[i].image; anim->delays[i] = internal->frames[i].delay; } } else { IMG_FreeAnimation(anim); anim = NULL; } } if (!anim) { SDL_OutOfMemory(); } SDL_free(internal->frames); SDL_free(internal); return anim; } return NULL; } #else /* Load a GIF type animation from an SDL datasource */ IMG_Animation *IMG_LoadGIFAnimation_RW(SDL_RWops *src) { return NULL; } #endif /* LOAD_GIF */ #if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) #ifdef LOAD_GIF /* See if an image is contained in a data source */ int IMG_isGIF(SDL_RWops *src) { Sint64 start; int is_GIF; char magic[6]; if ( !src ) return 0; start = SDL_RWtell(src); is_GIF = 0; if ( SDL_RWread(src, magic, sizeof(magic), 1) ) { if ( (SDL_strncmp(magic, "GIF", 3) == 0) && ((SDL_memcmp(magic + 3, "87a", 3) == 0) || (SDL_memcmp(magic + 3, "89a", 3) == 0)) ) { is_GIF = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_GIF); } /* Load a GIF type image from an SDL datasource */ SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src) { SDL_Surface *image = NULL; Anim_t *internal = IMG_LoadGIF_RW_Internal(src, SDL_FALSE); if (internal) { image = internal->frames[0].image; SDL_free(internal->frames); SDL_free(internal); } return image; } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif /* See if an image is contained in a data source */ int IMG_isGIF(SDL_RWops *src) { return(0); } /* Load a GIF type image from an SDL datasource */ SDL_Surface *IMG_LoadGIF_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_GIF */ #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */ SDL2_image-2.8.8/src/IMG_jpg.c0000664000000000000000000006134114751445211012547 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This is a JPEG image file loading framework */ #include "SDL_image.h" #include #include /* We'll have JPG save support by default */ #ifndef SDL_IMAGE_SAVE_JPG #define SDL_IMAGE_SAVE_JPG 1 #endif #if defined(USE_STBIMAGE) #undef WANT_JPEGLIB #elif defined(SDL_IMAGE_USE_COMMON_BACKEND) #define WANT_JPEGLIB #elif defined(SDL_IMAGE_USE_WIC_BACKEND) #undef WANT_JPEGLIB #elif defined(__APPLE__) && defined(JPG_USES_IMAGEIO) #undef WANT_JPEGLIB #else #define WANT_JPEGLIB #endif #ifdef LOAD_JPG #ifdef WANT_JPEGLIB #define USE_JPEGLIB #include #ifdef JPEG_TRUE /* MinGW version of jpeg-8.x renamed TRUE to JPEG_TRUE etc. */ typedef JPEG_boolean boolean; #define TRUE JPEG_TRUE #define FALSE JPEG_FALSE #endif /* Define this for fast loading and not as good image quality */ /*#define FAST_JPEG*/ /* Define this for quicker (but less perfect) JPEG identification */ #define FAST_IS_JPEG static struct { int loaded; void *handle; void (*jpeg_calc_output_dimensions) (j_decompress_ptr cinfo); void (*jpeg_CreateDecompress) (j_decompress_ptr cinfo, int version, size_t structsize); void (*jpeg_destroy_decompress) (j_decompress_ptr cinfo); boolean (*jpeg_finish_decompress) (j_decompress_ptr cinfo); int (*jpeg_read_header) (j_decompress_ptr cinfo, boolean require_image); JDIMENSION (*jpeg_read_scanlines) (j_decompress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION max_lines); boolean (*jpeg_resync_to_restart) (j_decompress_ptr cinfo, int desired); boolean (*jpeg_start_decompress) (j_decompress_ptr cinfo); void (*jpeg_CreateCompress) (j_compress_ptr cinfo, int version, size_t structsize); void (*jpeg_start_compress) (j_compress_ptr cinfo, boolean write_all_tables); void (*jpeg_set_quality) (j_compress_ptr cinfo, int quality, boolean force_baseline); void (*jpeg_set_defaults) (j_compress_ptr cinfo); JDIMENSION (*jpeg_write_scanlines) (j_compress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION num_lines); void (*jpeg_finish_compress) (j_compress_ptr cinfo); void (*jpeg_destroy_compress) (j_compress_ptr cinfo); struct jpeg_error_mgr * (*jpeg_std_error) (struct jpeg_error_mgr * err); } lib; #ifdef LOAD_JPG_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ lib.FUNC = (SIG) SDL_LoadFunction(lib.handle, #FUNC); \ if (lib.FUNC == NULL) { SDL_UnloadObject(lib.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ lib.FUNC = FUNC; #endif int IMG_InitJPG() { if ( lib.loaded == 0 ) { #ifdef LOAD_JPG_DYNAMIC lib.handle = SDL_LoadObject(LOAD_JPG_DYNAMIC); if ( lib.handle == NULL ) { return -1; } #endif FUNCTION_LOADER(jpeg_calc_output_dimensions, void (*) (j_decompress_ptr cinfo)) FUNCTION_LOADER(jpeg_CreateDecompress, void (*) (j_decompress_ptr cinfo, int version, size_t structsize)) FUNCTION_LOADER(jpeg_destroy_decompress, void (*) (j_decompress_ptr cinfo)) FUNCTION_LOADER(jpeg_finish_decompress, boolean (*) (j_decompress_ptr cinfo)) FUNCTION_LOADER(jpeg_read_header, int (*) (j_decompress_ptr cinfo, boolean require_image)) FUNCTION_LOADER(jpeg_read_scanlines, JDIMENSION (*) (j_decompress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION max_lines)) FUNCTION_LOADER(jpeg_resync_to_restart, boolean (*) (j_decompress_ptr cinfo, int desired)) FUNCTION_LOADER(jpeg_start_decompress, boolean (*) (j_decompress_ptr cinfo)) FUNCTION_LOADER(jpeg_CreateCompress, void (*) (j_compress_ptr cinfo, int version, size_t structsize)) FUNCTION_LOADER(jpeg_start_compress, void (*) (j_compress_ptr cinfo, boolean write_all_tables)) FUNCTION_LOADER(jpeg_set_quality, void (*) (j_compress_ptr cinfo, int quality, boolean force_baseline)) FUNCTION_LOADER(jpeg_set_defaults, void (*) (j_compress_ptr cinfo)) FUNCTION_LOADER(jpeg_write_scanlines, JDIMENSION (*) (j_compress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION num_lines)) FUNCTION_LOADER(jpeg_finish_compress, void (*) (j_compress_ptr cinfo)) FUNCTION_LOADER(jpeg_destroy_compress, void (*) (j_compress_ptr cinfo)) FUNCTION_LOADER(jpeg_std_error, struct jpeg_error_mgr * (*) (struct jpeg_error_mgr * err)) } ++lib.loaded; return 0; } void IMG_QuitJPG() { if ( lib.loaded == 0 ) { return; } if ( lib.loaded == 1 ) { #ifdef LOAD_JPG_DYNAMIC SDL_UnloadObject(lib.handle); #endif } --lib.loaded; } /* See if an image is contained in a data source */ int IMG_isJPG(SDL_RWops *src) { Sint64 start; int is_JPG; int in_scan; Uint8 magic[4]; /* This detection code is by Steaphan Greene */ /* Blame me, not Sam, if this doesn't work right. */ /* And don't forget to report the problem to the the sdl list too! */ if ( !src ) return 0; start = SDL_RWtell(src); is_JPG = 0; in_scan = 0; if ( SDL_RWread(src, magic, 2, 1) ) { if ( (magic[0] == 0xFF) && (magic[1] == 0xD8) ) { is_JPG = 1; while (is_JPG == 1) { if(SDL_RWread(src, magic, 1, 2) != 2) { is_JPG = 0; } else if( (magic[0] != 0xFF) && (in_scan == 0) ) { is_JPG = 0; } else if( (magic[0] != 0xFF) || (magic[1] == 0xFF) ) { /* Extra padding in JPEG (legal) */ /* or this is data and we are scanning */ SDL_RWseek(src, -1, RW_SEEK_CUR); } else if(magic[1] == 0xD9) { /* Got to end of good JPEG */ break; } else if( (in_scan == 1) && (magic[1] == 0x00) ) { /* This is an encoded 0xFF within the data */ } else if( (magic[1] >= 0xD0) && (magic[1] < 0xD9) ) { /* These have nothing else */ } else if(SDL_RWread(src, magic+2, 1, 2) != 2) { is_JPG = 0; } else { /* Yes, it's big-endian */ Sint64 innerStart; Uint32 size; Sint64 end; innerStart = SDL_RWtell(src); size = (magic[2] << 8) + magic[3]; end = SDL_RWseek(src, size-2, RW_SEEK_CUR); if ( end != innerStart + size - 2 ) is_JPG = 0; if ( magic[1] == 0xDA ) { /* Now comes the actual JPEG meat */ #ifdef FAST_IS_JPEG /* Ok, I'm convinced. It is a JPEG. */ break; #else /* I'm not convinced. Prove it! */ in_scan = 1; #endif } } } } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_JPG); } #define INPUT_BUFFER_SIZE 4096 typedef struct { struct jpeg_source_mgr pub; SDL_RWops *ctx; Uint8 buffer[INPUT_BUFFER_SIZE]; } my_source_mgr; /* * Initialize source --- called by jpeg_read_header * before any data is actually read. */ static void init_source (j_decompress_ptr cinfo) { /* We don't actually need to do anything */ (void)cinfo; return; } /* * Fill the input buffer --- called whenever buffer is emptied. */ static boolean fill_input_buffer (j_decompress_ptr cinfo) { my_source_mgr * src = (my_source_mgr *) cinfo->src; int nbytes; nbytes = (int)SDL_RWread(src->ctx, src->buffer, 1, INPUT_BUFFER_SIZE); if (nbytes <= 0) { /* Insert a fake EOI marker */ src->buffer[0] = (Uint8) 0xFF; src->buffer[1] = (Uint8) JPEG_EOI; nbytes = 2; } src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer = nbytes; return TRUE; } /* * Skip data --- used to skip over a potentially large amount of * uninteresting data (such as an APPn marker). * * Writers of suspendable-input applications must note that skip_input_data * is not granted the right to give a suspension return. If the skip extends * beyond the data currently in the buffer, the buffer can be marked empty so * that the next read will cause a fill_input_buffer call that can suspend. * Arranging for additional bytes to be discarded before reloading the input * buffer is the application writer's problem. */ static void skip_input_data (j_decompress_ptr cinfo, long num_bytes) { my_source_mgr * src = (my_source_mgr *) cinfo->src; /* Just a dumb implementation for now. Could use fseek() except * it doesn't work on pipes. Not clear that being smart is worth * any trouble anyway --- large skips are infrequent. */ if (num_bytes > 0) { while (num_bytes > (long) src->pub.bytes_in_buffer) { num_bytes -= (long) src->pub.bytes_in_buffer; (void) src->pub.fill_input_buffer(cinfo); /* note we assume that fill_input_buffer will never * return FALSE, so suspension need not be handled. */ } src->pub.next_input_byte += (size_t) num_bytes; src->pub.bytes_in_buffer -= (size_t) num_bytes; } } /* * Terminate source --- called by jpeg_finish_decompress * after all data has been read. */ static void term_source (j_decompress_ptr cinfo) { /* We don't actually need to do anything */ (void)cinfo; return; } /* * Prepare for input from a stdio stream. * The caller must have already opened the stream, and is responsible * for closing it after finishing decompression. */ static void jpeg_SDL_RW_src (j_decompress_ptr cinfo, SDL_RWops *ctx) { my_source_mgr *src; /* The source object and input buffer are made permanent so that a series * of JPEG images can be read from the same file by calling jpeg_stdio_src * only before the first one. (If we discarded the buffer at the end of * one image, we'd likely lose the start of the next one.) * This makes it unsafe to use this manager and a different source * manager serially with the same JPEG object. Caveat programmer. */ if (cinfo->src == NULL) { /* first time for this JPEG object? */ cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(my_source_mgr)); src = (my_source_mgr *) cinfo->src; } src = (my_source_mgr *) cinfo->src; src->pub.init_source = init_source; src->pub.fill_input_buffer = fill_input_buffer; src->pub.skip_input_data = skip_input_data; src->pub.resync_to_restart = lib.jpeg_resync_to_restart; /* use default method */ src->pub.term_source = term_source; src->ctx = ctx; src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ src->pub.next_input_byte = NULL; /* until buffer loaded */ } struct my_error_mgr { struct jpeg_error_mgr errmgr; jmp_buf escape; }; static void my_error_exit(j_common_ptr cinfo) { struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err; longjmp(err->escape, 1); } static void output_no_message(j_common_ptr cinfo) { /* do nothing */ (void)cinfo; } /* Load a JPEG type image from an SDL datasource */ SDL_Surface *IMG_LoadJPG_RW(SDL_RWops *src) { Sint64 start; struct jpeg_decompress_struct cinfo; JSAMPROW rowptr[1]; SDL_Surface *surface = NULL; struct my_error_mgr jerr; if ( !src ) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); if ( (IMG_Init(IMG_INIT_JPG) & IMG_INIT_JPG) == 0 ) { return NULL; } /* Create a decompression structure and load the JPEG header */ cinfo.err = lib.jpeg_std_error(&jerr.errmgr); jerr.errmgr.error_exit = my_error_exit; jerr.errmgr.output_message = output_no_message; #ifdef _MSC_VER #pragma warning(disable:4611) /* warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable */ #endif if(setjmp(jerr.escape)) { /* If we get here, libjpeg found an error */ lib.jpeg_destroy_decompress(&cinfo); if ( surface != NULL ) { SDL_FreeSurface(surface); } SDL_RWseek(src, start, RW_SEEK_SET); IMG_SetError("JPEG loading error"); return NULL; } lib.jpeg_create_decompress(&cinfo); jpeg_SDL_RW_src(&cinfo, src); lib.jpeg_read_header(&cinfo, TRUE); if(cinfo.num_components == 4) { /* Set 32-bit Raw output */ cinfo.out_color_space = JCS_CMYK; cinfo.quantize_colors = FALSE; lib.jpeg_calc_output_dimensions(&cinfo); /* Allocate an output surface to hold the image */ surface = SDL_CreateRGBSurfaceWithFormat(0, cinfo.output_width, cinfo.output_height, 0, SDL_PIXELFORMAT_BGRA32); } else { /* Set 24-bit RGB output */ cinfo.out_color_space = JCS_RGB; cinfo.quantize_colors = FALSE; #ifdef FAST_JPEG cinfo.scale_num = 1; cinfo.scale_denom = 1; cinfo.dct_method = JDCT_FASTEST; cinfo.do_fancy_upsampling = FALSE; #endif lib.jpeg_calc_output_dimensions(&cinfo); /* Allocate an output surface to hold the image */ surface = SDL_CreateRGBSurfaceWithFormat(0, cinfo.output_width, cinfo.output_height, 0, SDL_PIXELFORMAT_RGB24); } if ( surface == NULL ) { lib.jpeg_destroy_decompress(&cinfo); SDL_RWseek(src, start, RW_SEEK_SET); IMG_SetError("Out of memory"); return NULL; } /* Decompress the image */ lib.jpeg_start_decompress(&cinfo); while ( cinfo.output_scanline < cinfo.output_height ) { rowptr[0] = (JSAMPROW)(Uint8 *)surface->pixels + cinfo.output_scanline * surface->pitch; lib.jpeg_read_scanlines(&cinfo, rowptr, (JDIMENSION) 1); } lib.jpeg_finish_decompress(&cinfo); lib.jpeg_destroy_decompress(&cinfo); return(surface); } #define OUTPUT_BUFFER_SIZE 4096 typedef struct { struct jpeg_destination_mgr pub; SDL_RWops *ctx; Uint8 buffer[OUTPUT_BUFFER_SIZE]; } my_destination_mgr; static void init_destination(j_compress_ptr cinfo) { /* We don't actually need to do anything */ (void)cinfo; return; } static boolean empty_output_buffer(j_compress_ptr cinfo) { my_destination_mgr * dest = (my_destination_mgr *)cinfo->dest; /* In typical applications, it should write out the *entire* buffer */ SDL_RWwrite(dest->ctx, dest->buffer, 1, OUTPUT_BUFFER_SIZE); dest->pub.next_output_byte = dest->buffer; dest->pub.free_in_buffer = OUTPUT_BUFFER_SIZE; return TRUE; } static void term_destination(j_compress_ptr cinfo) { my_destination_mgr * dest = (my_destination_mgr *)cinfo->dest; /* In most applications, this must flush any data remaining in the buffer */ SDL_RWwrite(dest->ctx, dest->buffer, 1, OUTPUT_BUFFER_SIZE - dest->pub.free_in_buffer); } static void jpeg_SDL_RW_dest(j_compress_ptr cinfo, SDL_RWops *ctx) { my_destination_mgr *dest; if (cinfo->dest == NULL) { cinfo->dest = (struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_PERMANENT, sizeof(my_destination_mgr)); dest = (my_destination_mgr *)cinfo->dest; } dest = (my_destination_mgr *)cinfo->dest; dest->pub.init_destination = init_destination; dest->pub.empty_output_buffer = empty_output_buffer; dest->pub.term_destination = term_destination; dest->ctx = ctx; dest->pub.next_output_byte = dest->buffer; dest->pub.free_in_buffer = OUTPUT_BUFFER_SIZE; } struct savejpeg_vars { struct jpeg_compress_struct cinfo; struct my_error_mgr jerr; Sint64 original_offset; }; static int JPEG_SaveJPEG_RW(struct savejpeg_vars *vars, SDL_Surface *jpeg_surface, SDL_RWops *dst, int quality) { /* Create a compression structure and load the JPEG header */ vars->cinfo.err = lib.jpeg_std_error(&vars->jerr.errmgr); vars->jerr.errmgr.error_exit = my_error_exit; vars->jerr.errmgr.output_message = output_no_message; vars->original_offset = SDL_RWtell(dst); if(setjmp(vars->jerr.escape)) { /* If we get here, libjpeg found an error */ lib.jpeg_destroy_compress(&vars->cinfo); SDL_RWseek(dst, vars->original_offset, RW_SEEK_SET); return IMG_SetError("Error saving JPEG with libjpeg"); } lib.jpeg_create_compress(&vars->cinfo); jpeg_SDL_RW_dest(&vars->cinfo, dst); vars->cinfo.image_width = jpeg_surface->w; vars->cinfo.image_height = jpeg_surface->h; vars->cinfo.in_color_space = JCS_RGB; vars->cinfo.input_components = 3; lib.jpeg_set_defaults(&vars->cinfo); lib.jpeg_set_quality(&vars->cinfo, quality, TRUE); lib.jpeg_start_compress(&vars->cinfo, TRUE); while (vars->cinfo.next_scanline < vars->cinfo.image_height) { JSAMPROW row_pointer[1]; int offset = vars->cinfo.next_scanline * jpeg_surface->pitch; row_pointer[0] = ((Uint8*)jpeg_surface->pixels) + offset; lib.jpeg_write_scanlines(&vars->cinfo, row_pointer, 1); } lib.jpeg_finish_compress(&vars->cinfo); lib.jpeg_destroy_compress(&vars->cinfo); return 0; } static int IMG_SaveJPG_RW_jpeglib(SDL_Surface *surface, SDL_RWops *dst, int quality) { /* The JPEG library reads bytes in R,G,B order, so this is the right * encoding for either endianness */ struct savejpeg_vars vars; static const Uint32 jpg_format = SDL_PIXELFORMAT_RGB24; SDL_Surface* jpeg_surface = surface; int ret; if (!IMG_Init(IMG_INIT_JPG)) { return -1; } /* Convert surface to format we can save */ if (surface->format->format != jpg_format) { jpeg_surface = SDL_ConvertSurfaceFormat(surface, jpg_format, 0); if (!jpeg_surface) { return -1; } } SDL_zero(vars); ret = JPEG_SaveJPEG_RW(&vars, jpeg_surface, dst, quality); if (jpeg_surface != surface) { SDL_FreeSurface(jpeg_surface); } return ret; } #elif defined(USE_STBIMAGE) extern SDL_Surface *IMG_LoadSTB_RW(SDL_RWops *src); int IMG_InitJPG() { /* Nothing to load */ return 0; } void IMG_QuitJPG() { /* Nothing to unload */ } /* FIXME: This is a copypaste from JPEGLIB! Pull that out of the ifdefs */ /* Define this for quicker (but less perfect) JPEG identification */ #define FAST_IS_JPEG /* See if an image is contained in a data source */ int IMG_isJPG(SDL_RWops *src) { Sint64 start; int is_JPG; int in_scan; Uint8 magic[4]; /* This detection code is by Steaphan Greene */ /* Blame me, not Sam, if this doesn't work right. */ /* And don't forget to report the problem to the the sdl list too! */ if ( !src ) return 0; start = SDL_RWtell(src); is_JPG = 0; in_scan = 0; if ( SDL_RWread(src, magic, 2, 1) ) { if ( (magic[0] == 0xFF) && (magic[1] == 0xD8) ) { is_JPG = 1; while (is_JPG == 1) { if(SDL_RWread(src, magic, 1, 2) != 2) { is_JPG = 0; } else if( (magic[0] != 0xFF) && (in_scan == 0) ) { is_JPG = 0; } else if( (magic[0] != 0xFF) || (magic[1] == 0xFF) ) { /* Extra padding in JPEG (legal) */ /* or this is data and we are scanning */ SDL_RWseek(src, -1, RW_SEEK_CUR); } else if(magic[1] == 0xD9) { /* Got to end of good JPEG */ break; } else if( (in_scan == 1) && (magic[1] == 0x00) ) { /* This is an encoded 0xFF within the data */ } else if( (magic[1] >= 0xD0) && (magic[1] < 0xD9) ) { /* These have nothing else */ } else if(SDL_RWread(src, magic+2, 1, 2) != 2) { is_JPG = 0; } else { /* Yes, it's big-endian */ Sint64 innerStart; Uint32 size; Sint64 end; innerStart = SDL_RWtell(src); size = (magic[2] << 8) + magic[3]; end = SDL_RWseek(src, size-2, RW_SEEK_CUR); if ( end != innerStart + size - 2 ) is_JPG = 0; if ( magic[1] == 0xDA ) { /* Now comes the actual JPEG meat */ #ifdef FAST_IS_JPEG /* Ok, I'm convinced. It is a JPEG. */ break; #else /* I'm not convinced. Prove it! */ in_scan = 1; #endif } } } } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_JPG); } /* Load a JPEG type image from an SDL datasource */ SDL_Surface *IMG_LoadJPG_RW(SDL_RWops *src) { return IMG_LoadSTB_RW(src); } #endif /* WANT_JPEGLIB */ #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif int IMG_InitJPG() { IMG_SetError("JPEG images are not supported"); return(-1); } void IMG_QuitJPG() { } /* See if an image is contained in a data source */ int IMG_isJPG(SDL_RWops *src) { return(0); } /* Load a JPEG type image from an SDL datasource */ SDL_Surface *IMG_LoadJPG_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_JPG */ /* Use tinyjpeg as a fallback if we don't have a hard dependency on libjpeg */ #if SDL_IMAGE_SAVE_JPG && (defined(LOAD_JPG_DYNAMIC) || !defined(WANT_JPEGLIB)) #undef assert #define assert SDL_assert #undef memcpy #define memcpy SDL_memcpy #undef memset #define memset SDL_memset #define ceilf SDL_ceilf #define floorf SDL_floorf #define cosf SDL_cosf #define tje_log SDL_Log #define TJE_IMPLEMENTATION #include "tiny_jpeg.h" static void IMG_SaveJPG_RW_tinyjpeg_callback(void* context, void* data, int size) { SDL_RWwrite((SDL_RWops*) context, data, 1, size); } static int IMG_SaveJPG_RW_tinyjpeg(SDL_Surface *surface, SDL_RWops *dst, int quality) { /* The JPEG library reads bytes in R,G,B order, so this is the right * encoding for either endianness */ static const Uint32 jpg_format = SDL_PIXELFORMAT_RGB24; SDL_Surface* jpeg_surface = surface; int result = -1; /* Convert surface to format we can save */ if (surface->format->format != jpg_format) { jpeg_surface = SDL_ConvertSurfaceFormat(surface, jpg_format, 0); if (!jpeg_surface) { return -1; } } /* Quality for tinyjpeg is from 1-3: * 0 - 33 - Lowest quality * 34 - 66 - Middle quality * 67 - 100 - Highest quality */ if (quality < 34) quality = 1; else if (quality < 67) quality = 2; else quality = 3; result = tje_encode_with_func( IMG_SaveJPG_RW_tinyjpeg_callback, dst, quality, jpeg_surface->w, jpeg_surface->h, 3, jpeg_surface->pixels, jpeg_surface->pitch ) - 1; /* tinyjpeg returns 0 on error, 1 on success */ if (jpeg_surface != surface) { SDL_FreeSurface(jpeg_surface); } if (result < 0) { SDL_SetError("tinyjpeg error"); } return result; } #endif /* SDL_IMAGE_SAVE_JPG && (defined(LOAD_JPG_DYNAMIC) || !defined(WANT_JPEGLIB)) */ int IMG_SaveJPG(SDL_Surface *surface, const char *file, int quality) { SDL_RWops *dst = SDL_RWFromFile(file, "wb"); if (dst) { return IMG_SaveJPG_RW(surface, dst, 1, quality); } else { return -1; } } int IMG_SaveJPG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst, int quality) { int result = -1; if (!dst) { return IMG_SetError("Passed NULL dst"); } #if SDL_IMAGE_SAVE_JPG #ifdef USE_JPEGLIB if (result < 0) { result = IMG_SaveJPG_RW_jpeglib(surface, dst, quality); } #endif #if defined(LOAD_JPG_DYNAMIC) || !defined(WANT_JPEGLIB) if (result < 0) { result = IMG_SaveJPG_RW_tinyjpeg(surface, dst, quality); } #endif #else result = IMG_SetError("SDL_image built without JPEG save support"); #endif if (freedst) { SDL_RWclose(dst); } return result; } SDL2_image-2.8.8/src/IMG_jxl.c0000664000000000000000000002142214751445211012560 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This is a JXL image file loading framework */ #include "SDL_image.h" #ifdef LOAD_JXL #include static struct { int loaded; void *handle; JxlDecoder* (*JxlDecoderCreate)(const JxlMemoryManager* memory_manager); JxlDecoderStatus (*JxlDecoderSubscribeEvents)(JxlDecoder* dec, int events_wanted); JxlDecoderStatus (*JxlDecoderSetInput)(JxlDecoder* dec, const uint8_t* data, size_t size); JxlDecoderStatus (*JxlDecoderProcessInput)(JxlDecoder* dec); JxlDecoderStatus (*JxlDecoderGetBasicInfo)(const JxlDecoder* dec, JxlBasicInfo* info); JxlDecoderStatus (*JxlDecoderImageOutBufferSize)(const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size); JxlDecoderStatus (*JxlDecoderSetImageOutBuffer)(JxlDecoder* dec, const JxlPixelFormat* format, void* buffer, size_t size); void (*JxlDecoderDestroy)(JxlDecoder* dec); } lib; #ifdef LOAD_JXL_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ lib.FUNC = (SIG) SDL_LoadFunction(lib.handle, #FUNC); \ if (lib.FUNC == NULL) { SDL_UnloadObject(lib.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ lib.FUNC = FUNC; \ if (lib.FUNC == NULL) { IMG_SetError("Missing jxl.framework"); return -1; } #endif #ifdef __APPLE__ /* Need to turn off optimizations so weak framework load check works */ __attribute__ ((optnone)) #endif int IMG_InitJXL() { if ( lib.loaded == 0 ) { #ifdef LOAD_JXL_DYNAMIC lib.handle = SDL_LoadObject(LOAD_JXL_DYNAMIC); if ( lib.handle == NULL ) { return -1; } #endif FUNCTION_LOADER(JxlDecoderCreate, JxlDecoder* (*)(const JxlMemoryManager* memory_manager)) FUNCTION_LOADER(JxlDecoderSubscribeEvents, JxlDecoderStatus (*)(JxlDecoder* dec, int events_wanted)) FUNCTION_LOADER(JxlDecoderSetInput, JxlDecoderStatus (*)(JxlDecoder* dec, const uint8_t* data, size_t size)) FUNCTION_LOADER(JxlDecoderProcessInput, JxlDecoderStatus (*)(JxlDecoder* dec)) FUNCTION_LOADER(JxlDecoderGetBasicInfo, JxlDecoderStatus (*)(const JxlDecoder* dec, JxlBasicInfo* info)) FUNCTION_LOADER(JxlDecoderImageOutBufferSize, JxlDecoderStatus (*)(const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size)) FUNCTION_LOADER(JxlDecoderSetImageOutBuffer, JxlDecoderStatus (*)(JxlDecoder* dec, const JxlPixelFormat* format, void* buffer, size_t size)) FUNCTION_LOADER(JxlDecoderDestroy, void (*)(JxlDecoder* dec)) } ++lib.loaded; return 0; } void IMG_QuitJXL() { if ( lib.loaded == 0 ) { return; } if ( lib.loaded == 1 ) { #ifdef LOAD_JXL_DYNAMIC SDL_UnloadObject(lib.handle); #endif } --lib.loaded; } /* See if an image is contained in a data source */ int IMG_isJXL(SDL_RWops *src) { Sint64 start; int is_JXL; Uint8 magic[12]; if ( !src ) return 0; start = SDL_RWtell(src); is_JXL = 0; if ( SDL_RWread(src, magic, 2, 1) ) { if ( magic[0] == 0xFF && magic[1] == 0x0A ) { /* This is a JXL codestream */ is_JXL = 1; } else { if ( SDL_RWread(src, &magic[2], sizeof(magic) - 2, 1) ) { if ( magic[0] == 0x00 && magic[1] == 0x00 && magic[2] == 0x00 && magic[3] == 0x0C && magic[4] == 'J' && magic[5] == 'X' && magic[6] == 'L' && magic[7] == ' ' && magic[8] == 0x0D && magic[9] == 0x0A && magic[10] == 0x87 && magic[11] == 0x0A ) { /* This is a JXL container */ is_JXL = 1; } } } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_JXL); } /* Load a JXL type image from an SDL datasource */ SDL_Surface *IMG_LoadJXL_RW(SDL_RWops *src) { Sint64 start; unsigned char *data; size_t datasize; JxlDecoder *decoder = NULL; JxlBasicInfo info; JxlPixelFormat format = { 4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0 }; size_t outputsize; void *pixels = NULL; int pitch = 0; SDL_Surface *surface = NULL; if (!src) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); if ((IMG_Init(IMG_INIT_JXL) & IMG_INIT_JXL) == 0) { return NULL; } data = (unsigned char *)SDL_LoadFile_RW(src, &datasize, SDL_FALSE); if (!data) { return NULL; } decoder = lib.JxlDecoderCreate(NULL); if (!decoder) { IMG_SetError("Couldn't create JXL decoder"); goto done; } if (lib.JxlDecoderSubscribeEvents(decoder, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) { IMG_SetError("Couldn't subscribe to JXL events"); goto done; } if (lib.JxlDecoderSetInput(decoder, data, datasize) != JXL_DEC_SUCCESS) { IMG_SetError("Couldn't set JXL input"); goto done; } SDL_zero(info); for ( ; ; ) { JxlDecoderStatus status = lib.JxlDecoderProcessInput(decoder); switch (status ) { case JXL_DEC_ERROR: IMG_SetError("JXL decoder error"); goto done; case JXL_DEC_NEED_MORE_INPUT: IMG_SetError("Incomplete JXL image"); goto done; case JXL_DEC_BASIC_INFO: if (lib.JxlDecoderGetBasicInfo(decoder, &info) != JXL_DEC_SUCCESS) { IMG_SetError("Couldn't get JXL image info"); goto done; } break; case JXL_DEC_NEED_IMAGE_OUT_BUFFER: if (lib.JxlDecoderImageOutBufferSize(decoder, &format, &outputsize) != JXL_DEC_SUCCESS) { IMG_SetError("Couldn't get JXL image size"); goto done; } if (info.xsize == 0 || info.ysize == 0) { IMG_SetError("Couldn't get pixels for %dx%d JXL image", info.xsize, info.ysize); goto done; } if (pixels) { SDL_free(pixels); } pixels = SDL_malloc(outputsize); if (!pixels) { SDL_OutOfMemory(); goto done; } if ((outputsize / info.ysize) > SDL_MAX_SINT32) { SDL_OutOfMemory(); goto done; } pitch = (int)(outputsize / info.ysize); if (lib.JxlDecoderSetImageOutBuffer(decoder, &format, pixels, outputsize) != JXL_DEC_SUCCESS) { IMG_SetError("Couldn't set JXL output buffer"); goto done; } break; case JXL_DEC_FULL_IMAGE: /* We have a full image - in the case of an animation, keep decoding until the last frame */ break; case JXL_DEC_SUCCESS: /* All done! */ surface = SDL_CreateRGBSurfaceWithFormatFrom(pixels, info.xsize, info.ysize, 0, pitch, SDL_PIXELFORMAT_RGBA32); if (surface) { /* Let SDL manage the memory now */ pixels = NULL; surface->flags &= ~SDL_PREALLOC; } goto done; default: IMG_SetError("Unknown JXL decoding status: %d", status); goto done; } } done: if (decoder) { lib.JxlDecoderDestroy(decoder); } if (data) { SDL_free(data); } if (pixels) { SDL_free(pixels); } if (!surface) { SDL_RWseek(src, start, RW_SEEK_SET); } return surface; } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif int IMG_InitJXL() { IMG_SetError("JXL images are not supported"); return(-1); } void IMG_QuitJXL() { } /* See if an image is contained in a data source */ int IMG_isJXL(SDL_RWops *src) { return(0); } /* Load a JXL type image from an SDL datasource */ SDL_Surface *IMG_LoadJXL_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_JXL */ SDL2_image-2.8.8/src/IMG_lbm.c0000664000000000000000000004040014751445211012532 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This is a ILBM image file loading framework Load IFF pictures, PBM & ILBM packing methods, with or without stencil Written by Daniel Morais ( Daniel AT Morais DOT com ) in September 2001. 24 bits ILBM files support added by Marc Le Douarain (http://www.multimania.com/mavati) in December 2002. EHB and HAM (specific Amiga graphic chip modes) support added by Marc Le Douarain (http://www.multimania.com/mavati) in December 2003. Stencil and colorkey fixes by David Raulo (david.raulo AT free DOT fr) in February 2004. Buffer overflow fix in RLE decompression by David Raulo in January 2008. */ #include "SDL_endian.h" #include "SDL_image.h" #ifdef LOAD_LBM #define MAXCOLORS 256 /* Structure for an IFF picture ( BMHD = Bitmap Header ) */ typedef struct { Uint16 w, h; /* width & height of the bitmap in pixels */ Sint16 x, y; /* screen coordinates of the bitmap */ Uint8 planes; /* number of planes of the bitmap */ Uint8 mask; /* mask type ( 0 => no mask ) */ Uint8 tcomp; /* compression type */ Uint8 pad1; /* dummy value, for padding */ Uint16 tcolor; /* transparent color */ Uint8 xAspect, /* pixel aspect ratio */ yAspect; Sint16 Lpage; /* width of the screen in pixels */ Sint16 Hpage; /* height of the screen in pixels */ } BMHD; int IMG_isLBM( SDL_RWops *src ) { Sint64 start; int is_LBM; Uint8 magic[4+4+4]; if ( !src ) return 0; start = SDL_RWtell(src); is_LBM = 0; if ( SDL_RWread( src, magic, sizeof(magic), 1 ) ) { if ( !SDL_memcmp( magic, "FORM", 4 ) && ( !SDL_memcmp( magic + 8, "PBM ", 4 ) || !SDL_memcmp( magic + 8, "ILBM", 4 ) ) ) { is_LBM = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return( is_LBM ); } SDL_Surface *IMG_LoadLBM_RW( SDL_RWops *src ) { Sint64 start; SDL_Surface *Image; Uint8 id[4], pbm, colormap[MAXCOLORS*3], *MiniBuf, *ptr, count, color, msk; Uint32 size, bytesloaded, nbcolors; Uint32 i, j, bytesperline, nbplanes, stencil, plane, h; Uint32 remainingbytes; Uint32 width; BMHD bmhd; char *error; Uint8 flagHAM,flagEHB; Image = NULL; error = NULL; MiniBuf = NULL; if ( !src ) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); if ( !SDL_RWread( src, id, 4, 1 ) ) { error="error reading IFF chunk"; goto done; } /* Should be the size of the file minus 4+4 ( 'FORM'+size ) */ if ( !SDL_RWread( src, &size, 4, 1 ) ) { error="error reading IFF chunk size"; goto done; } /* As size is not used here, no need to swap it */ if ( SDL_memcmp( id, "FORM", 4 ) != 0 ) { error="not a IFF file"; goto done; } if ( !SDL_RWread( src, id, 4, 1 ) ) { error="error reading IFF chunk"; goto done; } pbm = 0; /* File format : PBM=Packed Bitmap, ILBM=Interleaved Bitmap */ if ( !SDL_memcmp( id, "PBM ", 4 ) ) pbm = 1; else if ( SDL_memcmp( id, "ILBM", 4 ) ) { error="not a IFF picture"; goto done; } nbcolors = 0; SDL_memset( &bmhd, 0, sizeof( BMHD ) ); flagHAM = 0; flagEHB = 0; while ( SDL_memcmp( id, "BODY", 4 ) != 0 ) { if ( !SDL_RWread( src, id, 4, 1 ) ) { error="error reading IFF chunk"; goto done; } if ( !SDL_RWread( src, &size, 4, 1 ) ) { error="error reading IFF chunk size"; goto done; } bytesloaded = 0; size = SDL_SwapBE32( size ); if ( !SDL_memcmp( id, "BMHD", 4 ) ) /* Bitmap header */ { if ( !SDL_RWread( src, &bmhd, sizeof( BMHD ), 1 ) ) { error="error reading BMHD chunk"; goto done; } bytesloaded = sizeof( BMHD ); bmhd.w = SDL_SwapBE16( bmhd.w ); bmhd.h = SDL_SwapBE16( bmhd.h ); bmhd.x = SDL_SwapBE16( bmhd.x ); bmhd.y = SDL_SwapBE16( bmhd.y ); bmhd.tcolor = SDL_SwapBE16( bmhd.tcolor ); bmhd.Lpage = SDL_SwapBE16( bmhd.Lpage ); bmhd.Hpage = SDL_SwapBE16( bmhd.Hpage ); } if ( !SDL_memcmp( id, "CMAP", 4 ) ) /* palette ( Color Map ) */ { if (size > sizeof (colormap)) { error="colormap size is too large"; goto done; } if ( !SDL_RWread( src, colormap, size, 1 ) ) { error="error reading CMAP chunk"; goto done; } bytesloaded = size; nbcolors = size / 3; } if ( !SDL_memcmp( id, "CAMG", 4 ) ) /* Amiga ViewMode */ { Uint32 viewmodes; if ( !SDL_RWread( src, &viewmodes, sizeof(viewmodes), 1 ) ) { error="error reading CAMG chunk"; goto done; } bytesloaded = size; viewmodes = SDL_SwapBE32( viewmodes ); if ( viewmodes & 0x0800 ) flagHAM = 1; if ( viewmodes & 0x0080 ) flagEHB = 1; } if ( SDL_memcmp( id, "BODY", 4 ) ) { if ( size & 1 ) ++size; /* padding ! */ size -= bytesloaded; /* skip the remaining bytes of this chunk */ if ( size ) SDL_RWseek( src, size, RW_SEEK_CUR ); } } /* compute some useful values, based on the bitmap header */ width = ( bmhd.w + 15 ) & 0xFFFFFFF0; /* Width in pixels modulo 16 */ bytesperline = ( ( bmhd.w + 15 ) / 16 ) * 2; nbplanes = bmhd.planes; if ( pbm ) /* File format : 'Packed Bitmap' */ { bytesperline *= 8; nbplanes = 1; } stencil = (bmhd.mask & 1); /* There is a mask ( 'stencil' ) */ /* Allocate memory for a temporary buffer ( used for decompression/deinterleaving ) */ MiniBuf = (Uint8 *)SDL_malloc( bytesperline * (nbplanes + stencil) ); if ( MiniBuf == NULL ) { error="not enough memory for temporary buffer"; goto done; } { Uint32 format = SDL_PIXELFORMAT_INDEX8; if (nbplanes == 24 || flagHAM == 1) { #if SDL_BYTEORDER == SDL_BIG_ENDIAN format = SDL_PIXELFORMAT_RGB24; #else format = SDL_PIXELFORMAT_BGR24; #endif } if ((Image = SDL_CreateRGBSurfaceWithFormat(0, width, bmhd.h, 0, format)) == NULL){ goto done; } } if ( bmhd.mask & 2 ) /* There is a transparent color */ SDL_SetColorKey( Image, SDL_TRUE, bmhd.tcolor ); /* Update palette information */ /* There is no palette in 24 bits ILBM file */ if ( nbcolors>0 && flagHAM==0 ) { /* FIXME: Should this include the stencil? See comment below */ int nbrcolorsfinal = 1 << (nbplanes + stencil); ptr = &colormap[0]; for ( i=0; iformat->palette->colors[i].r = *ptr++; Image->format->palette->colors[i].g = *ptr++; Image->format->palette->colors[i].b = *ptr++; } /* Amiga EHB mode (Extra-Half-Bright) */ /* 6 bitplanes mode with a 32 colors palette */ /* The 32 last colors are the same but divided by 2 */ /* Some Amiga pictures save 64 colors with 32 last wrong colors, */ /* they shouldn't !, and here we overwrite these 32 bad colors. */ if ( (nbcolors==32 || flagEHB ) && (1<format->palette->colors[i].r = (*ptr++)/2; Image->format->palette->colors[i].g = (*ptr++)/2; Image->format->palette->colors[i].b = (*ptr++)/2; } } /* If nbcolors < 2^nbplanes, repeat the colormap */ /* This happens when pictures have a stencil mask */ if ( nbrcolorsfinal > (1<format->palette->colors[i].r = Image->format->palette->colors[i%nbcolors].r; Image->format->palette->colors[i].g = Image->format->palette->colors[i%nbcolors].g; Image->format->palette->colors[i].b = Image->format->palette->colors[i%nbcolors].b; } if ( !pbm ) Image->format->palette->ncolors = nbrcolorsfinal; } /* Get the bitmap */ for ( h=0; h < bmhd.h; h++ ) { /* uncompress the datas of each planes */ for ( plane=0; plane < (nbplanes+stencil); plane++ ) { ptr = MiniBuf + ( plane * bytesperline ); remainingbytes = bytesperline; if ( bmhd.tcomp == 1 ) /* Datas are compressed */ { do { if ( !SDL_RWread( src, &count, 1, 1 ) ) { error="error reading BODY chunk"; goto done; } if ( count & 0x80 ) { count ^= 0xFF; count += 2; /* now it */ if ( ( count > remainingbytes ) || !SDL_RWread( src, &color, 1, 1 ) ) { error="error reading BODY chunk"; goto done; } SDL_memset( ptr, color, count ); } else { ++count; if ( ( count > remainingbytes ) || !SDL_RWread( src, ptr, count, 1 ) ) { error="error reading BODY chunk"; goto done; } } ptr += count; remainingbytes -= count; } while ( remainingbytes > 0 ); } else { if ( !SDL_RWread( src, ptr, bytesperline, 1 ) ) { error="error reading BODY chunk"; goto done; } } } /* One line has been read, store it ! */ ptr = (Uint8 *)Image->pixels; if ( nbplanes==24 || flagHAM==1 ) ptr += h * width * 3; else ptr += h * width; if ( pbm ) /* File format : 'Packed Bitmap' */ { SDL_memcpy( ptr, MiniBuf, width ); } else /* We have to un-interlace the bits ! */ { if ( nbplanes!=24 && flagHAM==0 ) { size = ( width + 7 ) / 8; for ( i=0; i < size; i++ ) { SDL_memset( ptr, 0, 8 ); for ( plane=0; plane < (nbplanes + stencil); plane++ ) { color = *( MiniBuf + i + ( plane * bytesperline ) ); msk = 0x80; for ( j=0; j<8; j++ ) { if ( ( plane + j ) <= 7 ) ptr[j] |= (Uint8)( color & msk ) >> ( 7 - plane - j ); else ptr[j] |= (Uint8)( color & msk ) << ( plane + j - 7 ); msk >>= 1; } } ptr += 8; } } else { Uint32 finalcolor = 0; size = ( width + 7 ) / 8; /* 24 bitplanes ILBM : R0...R7,G0...G7,B0...B7 */ /* or HAM (6 bitplanes) or HAM8 (8 bitplanes) modes */ for ( i=0; i>(nbplanes-2) ) { case 0: /* take direct color from palette */ finalcolor = colormap[ pixelcolor*3 ] + (colormap[ pixelcolor*3+1 ]<<8) + (colormap[ pixelcolor*3+2 ]<<16); break; case 1: /* modify only blue component */ finalcolor = finalcolor&0x00FFFF; finalcolor = finalcolor | (pixelcolor<<(16+(10-nbplanes))); break; case 2: /* modify only red component */ finalcolor = finalcolor&0xFFFF00; finalcolor = finalcolor | pixelcolor<<(10-nbplanes); break; case 3: /* modify only green component */ finalcolor = finalcolor&0xFF00FF; finalcolor = finalcolor | (pixelcolor<<(8+(10-nbplanes))); break; } } else { finalcolor = pixelcolor; } #if SDL_BYTEORDER == SDL_LIL_ENDIAN *ptr++ = (Uint8)(finalcolor>>16); *ptr++ = (Uint8)(finalcolor>>8); *ptr++ = (Uint8)(finalcolor); #else *ptr++ = (Uint8)(finalcolor); *ptr++ = (Uint8)(finalcolor>>8); *ptr++ = (Uint8)(finalcolor>>16); #endif maskBit = maskBit>>1; } } } } } done: if ( MiniBuf ) SDL_free( MiniBuf ); if ( error ) { SDL_RWseek(src, start, RW_SEEK_SET); if ( Image ) { SDL_FreeSurface( Image ); Image = NULL; } IMG_SetError( "%s", error ); } return( Image ); } #else /* LOAD_LBM */ #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif /* See if an image is contained in a data source */ int IMG_isLBM(SDL_RWops *src) { return(0); } /* Load an IFF type image from an SDL datasource */ SDL_Surface *IMG_LoadLBM_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_LBM */ SDL2_image-2.8.8/src/IMG_pcx.c0000664000000000000000000002174714751445211012567 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* * PCX file reader: * Supports: * 1..4 bits/pixel in multiplanar format (1 bit/plane/pixel) * 8 bits/pixel in single-planar format (8 bits/plane/pixel) * 24 bits/pixel in 3-plane format (8 bits/plane/pixel) * * (The <8bpp formats are expanded to 8bpp surfaces) * * Doesn't support: * single-planar packed-pixel formats other than 8bpp * 4-plane 32bpp format with a fourth "intensity" plane */ #include "SDL_endian.h" #include "SDL_image.h" #ifdef LOAD_PCX struct PCXheader { Uint8 Manufacturer; Uint8 Version; Uint8 Encoding; Uint8 BitsPerPixel; Sint16 Xmin, Ymin, Xmax, Ymax; Sint16 HDpi, VDpi; Uint8 Colormap[48]; Uint8 Reserved; Uint8 NPlanes; Sint16 BytesPerLine; Sint16 PaletteInfo; Sint16 HscreenSize; Sint16 VscreenSize; Uint8 Filler[54]; }; /* See if an image is contained in a data source */ int IMG_isPCX(SDL_RWops *src) { Sint64 start; int is_PCX; const int ZSoft_Manufacturer = 10; const int PC_Paintbrush_Version = 5; const int PCX_Uncompressed_Encoding = 0; const int PCX_RunLength_Encoding = 1; struct PCXheader pcxh; if ( !src ) return 0; start = SDL_RWtell(src); is_PCX = 0; if ( SDL_RWread(src, &pcxh, sizeof(pcxh), 1) == 1 ) { if ( (pcxh.Manufacturer == ZSoft_Manufacturer) && (pcxh.Version == PC_Paintbrush_Version) && (pcxh.Encoding == PCX_RunLength_Encoding || pcxh.Encoding == PCX_Uncompressed_Encoding) ) { is_PCX = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_PCX); } /* Load a PCX type image from an SDL datasource */ SDL_Surface *IMG_LoadPCX_RW(SDL_RWops *src) { Sint64 start; struct PCXheader pcxh; SDL_Surface *surface = NULL; int width, height; int y, bpl; Uint8 *row, *buf = NULL; char *error = NULL; int bits, src_bits; int count = 0; Uint8 ch; Uint32 format; if ( !src ) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); if ( !SDL_RWread(src, &pcxh, sizeof(pcxh), 1) ) { error = "file truncated"; goto done; } pcxh.Xmin = SDL_SwapLE16(pcxh.Xmin); pcxh.Ymin = SDL_SwapLE16(pcxh.Ymin); pcxh.Xmax = SDL_SwapLE16(pcxh.Xmax); pcxh.Ymax = SDL_SwapLE16(pcxh.Ymax); pcxh.BytesPerLine = SDL_SwapLE16(pcxh.BytesPerLine); #if 0 printf("Manufacturer = %d\n", pcxh.Manufacturer); printf("Version = %d\n", pcxh.Version); printf("Encoding = %d\n", pcxh.Encoding); printf("BitsPerPixel = %d\n", pcxh.BitsPerPixel); printf("Xmin = %d, Ymin = %d, Xmax = %d, Ymax = %d\n", pcxh.Xmin, pcxh.Ymin, pcxh.Xmax, pcxh.Ymax); printf("HDpi = %d, VDpi = %d\n", pcxh.HDpi, pcxh.VDpi); printf("NPlanes = %d\n", pcxh.NPlanes); printf("BytesPerLine = %d\n", pcxh.BytesPerLine); printf("PaletteInfo = %d\n", pcxh.PaletteInfo); printf("HscreenSize = %d\n", pcxh.HscreenSize); printf("VscreenSize = %d\n", pcxh.VscreenSize); #endif /* Create the surface of the appropriate type */ width = (pcxh.Xmax - pcxh.Xmin) + 1; height = (pcxh.Ymax - pcxh.Ymin) + 1; src_bits = pcxh.BitsPerPixel * pcxh.NPlanes; if((pcxh.BitsPerPixel == 1 && pcxh.NPlanes >= 1 && pcxh.NPlanes <= 4) || (pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 1)) { bits = 8; format = SDL_PIXELFORMAT_INDEX8; } else if(pcxh.BitsPerPixel == 8 && pcxh.NPlanes == 3) { bits = 24; format = SDL_PIXELFORMAT_RGB24; } else { error = "unsupported PCX format"; goto done; } surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 0, format); if ( surface == NULL ) { goto done; } bpl = pcxh.NPlanes * pcxh.BytesPerLine; buf = (Uint8 *)SDL_calloc(bpl, 1); if ( !buf ) { error = "Out of memory"; goto done; } row = (Uint8 *)surface->pixels; for ( y=0; yh; ++y ) { /* decode a scan line to a temporary buffer first */ int i; if ( pcxh.Encoding == 0 ) { if ( !SDL_RWread(src, buf, bpl, 1) ) { error = "file truncated"; goto done; } } else { for ( i = 0; i < bpl; i++ ) { if ( !count ) { if ( !SDL_RWread(src, &ch, 1, 1) ) { error = "file truncated"; goto done; } if ( ch < 0xc0 ) { count = 1; } else { count = ch - 0xc0; if( !SDL_RWread(src, &ch, 1, 1) ) { error = "file truncated"; goto done; } } } buf[i] = ch; count--; } } if ( src_bits <= 4 ) { /* expand planes to 1 byte/pixel */ Uint8 *innerSrc = buf; int plane; for ( plane = 0; plane < pcxh.NPlanes; plane++ ) { int j, k, x = 0; for( j = 0; j < pcxh.BytesPerLine; j++ ) { Uint8 byte = *innerSrc++; for( k = 7; k >= 0; k-- ) { unsigned bit = (byte >> k) & 1; /* skip padding bits */ if (j * 8 + k >= width) continue; row[x++] |= bit << plane; } } } } else if ( src_bits == 8 ) { /* Copy the row directly */ SDL_memcpy(row, buf, SDL_min(width, bpl)); } else if ( src_bits == 24 ) { /* de-interlace planes */ Uint8 *innerSrc = buf; Uint8 *end1 = buf+bpl; int plane; for ( plane = 0; plane < pcxh.NPlanes; plane++ ) { int x; Uint8 *dst = row + plane; Uint8 *end2= row + surface->pitch; for ( x = 0; x < width; x++ ) { if ( (innerSrc + x) >= end1 || dst >= end2 ) { error = "decoding out of bounds (corrupt?)"; goto done; } *dst = innerSrc[x]; dst += pcxh.NPlanes; } innerSrc += pcxh.BytesPerLine; } } row += surface->pitch; } if ( bits == 8 ) { SDL_Color *colors = surface->format->palette->colors; int nc = 1 << src_bits; int i; surface->format->palette->ncolors = nc; if ( src_bits == 8 ) { Uint8 ch; /* look for a 256-colour palette */ do { if ( !SDL_RWread(src, &ch, 1, 1) ) { /* Couldn't find the palette, try the end of the file */ SDL_RWseek(src, -768, RW_SEEK_END); break; } } while ( ch != 12 ); for ( i = 0; i < 256; i++ ) { SDL_RWread(src, &colors[i].r, 1, 1); SDL_RWread(src, &colors[i].g, 1, 1); SDL_RWread(src, &colors[i].b, 1, 1); } } else { for ( i = 0; i < nc; i++ ) { colors[i].r = pcxh.Colormap[i * 3]; colors[i].g = pcxh.Colormap[i * 3 + 1]; colors[i].b = pcxh.Colormap[i * 3 + 2]; } } } done: SDL_free(buf); if ( error ) { SDL_RWseek(src, start, RW_SEEK_SET); if ( surface ) { SDL_FreeSurface(surface); surface = NULL; } IMG_SetError("%s", error); } return(surface); } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif /* See if an image is contained in a data source */ int IMG_isPCX(SDL_RWops *src) { return(0); } /* Load a PCX type image from an SDL datasource */ SDL_Surface *IMG_LoadPCX_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_PCX */ SDL2_image-2.8.8/src/IMG_png.c0000664000000000000000000006760014751445211012557 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This is a PNG image file loading framework */ #include "SDL_image.h" /* We'll have PNG save support by default */ #if !defined(SDL_IMAGE_SAVE_PNG) # define SDL_IMAGE_SAVE_PNG 1 #endif #if defined(USE_STBIMAGE) #undef WANT_LIBPNG #elif defined(SDL_IMAGE_USE_COMMON_BACKEND) #define WANT_LIBPNG #elif defined(SDL_IMAGE_USE_WIC_BACKEND) #undef WANT_LIBPNG #elif defined(__APPLE__) && defined(PNG_USES_IMAGEIO) #undef WANT_LIBPNG #else #define WANT_LIBPNG #endif #ifdef LOAD_PNG #ifdef WANT_LIBPNG #define USE_LIBPNG /* This code was originally written by Philippe Lavoie (2 November 1998) */ #include "SDL_endian.h" #ifdef macintosh #define MACOS #endif #include /* Check for the older version of libpng */ #if (PNG_LIBPNG_VER_MAJOR == 1) #if (PNG_LIBPNG_VER_MINOR < 5) #define LIBPNG_VERSION_12 typedef png_bytep png_const_bytep; typedef png_color *png_const_colorp; typedef png_color_16 *png_const_color_16p; #endif #if (PNG_LIBPNG_VER_MINOR < 4) typedef png_structp png_const_structp; typedef png_infop png_const_infop; #endif #if (PNG_LIBPNG_VER_MINOR < 6) typedef png_structp png_structrp; typedef png_infop png_inforp; typedef png_const_structp png_const_structrp; typedef png_const_infop png_const_inforp; /* noconst15: version < 1.6 doesn't have const, >= 1.6 adds it */ /* noconst16: version < 1.6 does have const, >= 1.6 removes it */ typedef png_structp png_noconst15_structrp; typedef png_inforp png_noconst15_inforp; typedef png_const_inforp png_noconst16_inforp; #else typedef png_const_structp png_noconst15_structrp; typedef png_const_inforp png_noconst15_inforp; typedef png_inforp png_noconst16_inforp; #endif #else typedef png_const_structp png_noconst15_structrp; typedef png_const_inforp png_noconst15_inforp; typedef png_inforp png_noconst16_inforp; #endif static struct { int loaded; void *handle; png_infop (*png_create_info_struct) (png_noconst15_structrp png_ptr); png_structp (*png_create_read_struct) (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn); void (*png_destroy_read_struct) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr); png_uint_32 (*png_get_IHDR) (png_noconst15_structrp png_ptr, png_noconst15_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, int *bit_depth, int *color_type, int *interlace_method, int *compression_method, int *filter_method); png_voidp (*png_get_io_ptr) (png_noconst15_structrp png_ptr); png_byte (*png_get_channels) (png_const_structrp png_ptr, png_const_inforp info_ptr); png_uint_32 (*png_get_PLTE) (png_const_structrp png_ptr, png_noconst16_inforp info_ptr, png_colorp *palette, int *num_palette); png_uint_32 (*png_get_tRNS) (png_const_structrp png_ptr, png_inforp info_ptr, png_bytep *trans, int *num_trans, png_color_16p *trans_values); png_uint_32 (*png_get_valid) (png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 flag); void (*png_read_image) (png_structrp png_ptr, png_bytepp image); void (*png_read_info) (png_structrp png_ptr, png_inforp info_ptr); void (*png_read_update_info) (png_structrp png_ptr, png_inforp info_ptr); void (*png_set_expand) (png_structrp png_ptr); void (*png_set_gray_to_rgb) (png_structrp png_ptr); void (*png_set_packing) (png_structrp png_ptr); void (*png_set_read_fn) (png_structrp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn); void (*png_set_strip_16) (png_structrp png_ptr); int (*png_set_interlace_handling) (png_structrp png_ptr); int (*png_sig_cmp) (png_const_bytep sig, png_size_t start, png_size_t num_to_check); #ifdef PNG_SETJMP_SUPPORTED #ifndef LIBPNG_VERSION_12 jmp_buf* (*png_set_longjmp_fn) (png_structrp, png_longjmp_ptr, size_t); #endif #endif #if SDL_IMAGE_SAVE_PNG png_structp (*png_create_write_struct) (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn); void (*png_destroy_write_struct) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr); void (*png_set_write_fn) (png_structrp png_ptr, png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn); void (*png_set_IHDR) (png_noconst15_structrp png_ptr, png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, int interlace_type, int compression_type, int filter_type); void (*png_write_info) (png_structrp png_ptr, png_noconst15_inforp info_ptr); void (*png_set_rows) (png_noconst15_structrp png_ptr, png_inforp info_ptr, png_bytepp row_pointers); void (*png_write_png) (png_structrp png_ptr, png_inforp info_ptr, int transforms, png_voidp params); void (*png_set_PLTE) (png_structrp png_ptr, png_inforp info_ptr, png_const_colorp palette, int num_palette); void (*png_set_tRNS) (png_structrp png_ptr, png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, png_const_color_16p trans_color); #endif } lib; #ifdef LOAD_PNG_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ lib.FUNC = (SIG) SDL_LoadFunction(lib.handle, #FUNC); \ if (lib.FUNC == NULL) { SDL_UnloadObject(lib.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ lib.FUNC = FUNC; #endif int IMG_InitPNG() { if ( lib.loaded == 0 ) { #ifdef LOAD_PNG_DYNAMIC lib.handle = SDL_LoadObject(LOAD_PNG_DYNAMIC); if ( lib.handle == NULL ) { return -1; } #endif FUNCTION_LOADER(png_create_info_struct, png_infop (*) (png_noconst15_structrp png_ptr)) FUNCTION_LOADER(png_create_read_struct, png_structp (*) (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn)) FUNCTION_LOADER(png_destroy_read_struct, void (*) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)) FUNCTION_LOADER(png_get_IHDR, png_uint_32 (*) (png_noconst15_structrp png_ptr, png_noconst15_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, int *bit_depth, int *color_type, int *interlace_method, int *compression_method, int *filter_method)) FUNCTION_LOADER(png_get_io_ptr, png_voidp (*) (png_noconst15_structrp png_ptr)) FUNCTION_LOADER(png_get_channels, png_byte (*) (png_const_structrp png_ptr, png_const_inforp info_ptr)) FUNCTION_LOADER(png_get_PLTE, png_uint_32 (*) (png_const_structrp png_ptr, png_noconst16_inforp info_ptr, png_colorp *palette, int *num_palette)) FUNCTION_LOADER(png_get_tRNS, png_uint_32 (*) (png_const_structrp png_ptr, png_inforp info_ptr, png_bytep *trans, int *num_trans, png_color_16p *trans_values)) FUNCTION_LOADER(png_get_valid, png_uint_32 (*) (png_const_structrp png_ptr, png_const_inforp info_ptr, png_uint_32 flag)) FUNCTION_LOADER(png_read_image, void (*) (png_structrp png_ptr, png_bytepp image)) FUNCTION_LOADER(png_read_info, void (*) (png_structrp png_ptr, png_inforp info_ptr)) FUNCTION_LOADER(png_read_update_info, void (*) (png_structrp png_ptr, png_inforp info_ptr)) FUNCTION_LOADER(png_set_expand, void (*) (png_structrp png_ptr)) FUNCTION_LOADER(png_set_gray_to_rgb, void (*) (png_structrp png_ptr)) FUNCTION_LOADER(png_set_packing, void (*) (png_structrp png_ptr)) FUNCTION_LOADER(png_set_read_fn, void (*) (png_structrp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn)) FUNCTION_LOADER(png_set_strip_16, void (*) (png_structrp png_ptr)) FUNCTION_LOADER(png_set_interlace_handling, int (*) (png_structrp png_ptr)) FUNCTION_LOADER(png_sig_cmp, int (*) (png_const_bytep sig, png_size_t start, png_size_t num_to_check)) #ifdef PNG_SETJMP_SUPPORTED #ifndef LIBPNG_VERSION_12 FUNCTION_LOADER(png_set_longjmp_fn, jmp_buf* (*) (png_structrp, png_longjmp_ptr, size_t)) #endif #endif #if SDL_IMAGE_SAVE_PNG FUNCTION_LOADER(png_create_write_struct, png_structp (*) (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warn_fn)) FUNCTION_LOADER(png_destroy_write_struct, void (*) (png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)) FUNCTION_LOADER(png_set_write_fn, void (*) (png_structrp png_ptr, png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)) FUNCTION_LOADER(png_set_IHDR, void (*) (png_noconst15_structrp png_ptr, png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, int interlace_type, int compression_type, int filter_type)) FUNCTION_LOADER(png_write_info, void (*) (png_structrp png_ptr, png_noconst15_inforp info_ptr)) FUNCTION_LOADER(png_set_rows, void (*) (png_noconst15_structrp png_ptr, png_inforp info_ptr, png_bytepp row_pointers)) FUNCTION_LOADER(png_write_png, void (*) (png_structrp png_ptr, png_inforp info_ptr, int transforms, png_voidp params)) FUNCTION_LOADER(png_set_PLTE, void (*) (png_structrp png_ptr, png_inforp info_ptr, png_const_colorp palette, int num_palette)) FUNCTION_LOADER(png_set_tRNS, void (*) (png_structrp png_ptr, png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, png_const_color_16p trans_color)) #endif } ++lib.loaded; return 0; } void IMG_QuitPNG() { if ( lib.loaded == 0 ) { return; } if ( lib.loaded == 1 ) { #ifdef LOAD_PNG_DYNAMIC SDL_UnloadObject(lib.handle); #endif } --lib.loaded; } /* See if an image is contained in a data source */ int IMG_isPNG(SDL_RWops *src) { Sint64 start; int is_PNG; Uint8 magic[4]; if ( !src ) { return 0; } start = SDL_RWtell(src); is_PNG = 0; if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) { if ( magic[0] == 0x89 && magic[1] == 'P' && magic[2] == 'N' && magic[3] == 'G' ) { is_PNG = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_PNG); } /* Load a PNG type image from an SDL datasource */ static void png_read_data(png_structp ctx, png_bytep area, png_size_t size) { SDL_RWops *src; src = (SDL_RWops *)lib.png_get_io_ptr(ctx); SDL_RWread(src, area, size, 1); } struct loadpng_vars { const char *error; SDL_Surface *surface; png_structp png_ptr; png_infop info_ptr; png_bytep *row_pointers; }; static void LIBPNG_LoadPNG_RW(SDL_RWops *src, struct loadpng_vars *vars) { png_uint_32 width, height; int bit_depth, color_type, interlace_type, num_channels; Uint32 format; SDL_Palette *palette; int row, i; int ckey; png_color_16 *transv; /* Create the PNG loading context structure */ vars->png_ptr = lib.png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,NULL,NULL); if (vars->png_ptr == NULL) { vars->error = "Couldn't allocate memory for PNG file or incompatible PNG dll"; return; } /* Allocate/initialize the memory for image information. REQUIRED. */ vars->info_ptr = lib.png_create_info_struct(vars->png_ptr); if (vars->info_ptr == NULL) { vars->error = "Couldn't create image information for PNG file"; return; } /* Set error handling if you are using setjmp/longjmp method (this is * the normal method of doing things with libpng). REQUIRED unless you * set up your own error handlers in png_create_read_struct() earlier. */ #ifdef PNG_SETJMP_SUPPORTED #ifdef _MSC_VER #pragma warning(disable:4611) /* warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable */ #endif #ifndef LIBPNG_VERSION_12 if (setjmp(*lib.png_set_longjmp_fn(vars->png_ptr, longjmp, sizeof(jmp_buf)))) #else if (setjmp(vars->png_ptr->jmpbuf)) #endif { vars->error = "Error reading the PNG file."; return; } #endif /* Set up the input control */ lib.png_set_read_fn(vars->png_ptr, src, png_read_data); /* Read PNG header info */ lib.png_read_info(vars->png_ptr, vars->info_ptr); lib.png_get_IHDR(vars->png_ptr, vars->info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); /* tell libpng to strip 16 bit/color files down to 8 bits/color */ lib.png_set_strip_16(vars->png_ptr); /* tell libpng to de-interlace (if the image is interlaced) */ lib.png_set_interlace_handling(vars->png_ptr); /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */ lib.png_set_packing(vars->png_ptr); /* scale greyscale values to the range 0..255 */ if (color_type == PNG_COLOR_TYPE_GRAY) { lib.png_set_expand(vars->png_ptr); } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { lib.png_set_gray_to_rgb(vars->png_ptr); } /* For images with a single "transparent colour", set colour key; if more than one index has transparency, or if partially transparent entries exist, use full alpha channel */ ckey = -1; if (lib.png_get_valid(vars->png_ptr, vars->info_ptr, PNG_INFO_tRNS)) { int num_trans; Uint8 *trans; lib.png_get_tRNS(vars->png_ptr, vars->info_ptr, &trans, &num_trans, &transv); if (color_type == PNG_COLOR_TYPE_PALETTE) { /* Check if all tRNS entries are opaque except one */ int j, t = -1; for (j = 0; j < num_trans; j++) { if (trans[j] == 0) { if (t >= 0) { break; } t = j; } else if (trans[j] != 255) { break; } } if (j == num_trans) { /* exactly one transparent index */ ckey = t; } else { /* more than one transparent index, or translucency */ lib.png_set_expand(vars->png_ptr); } } else if (color_type == PNG_COLOR_TYPE_GRAY) { /* This will be turned into PNG_COLOR_TYPE_GRAY_ALPHA, so expand to RGBA */ lib.png_set_gray_to_rgb(vars->png_ptr); } else { ckey = 0; /* actual value will be set later */ } } lib.png_read_update_info(vars->png_ptr, vars->info_ptr); lib.png_get_IHDR(vars->png_ptr, vars->info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); /* Allocate the SDL surface to hold the image */ num_channels = lib.png_get_channels(vars->png_ptr, vars->info_ptr); format = SDL_PIXELFORMAT_UNKNOWN; if (num_channels == 3) { format = SDL_PIXELFORMAT_RGB24; } else if (num_channels == 4) { format = SDL_PIXELFORMAT_RGBA32; } else { /* Not sure they are all supported by png */ switch (bit_depth * num_channels) { case 1: format = SDL_PIXELFORMAT_INDEX1MSB; break; case 4: format = SDL_PIXELFORMAT_INDEX4MSB; break; case 8: format = SDL_PIXELFORMAT_INDEX8; break; case 12: format = SDL_PIXELFORMAT_RGB444; break; case 15: format = SDL_PIXELFORMAT_RGB555; break; case 16: format = SDL_PIXELFORMAT_RGB565; break; default: break; } } vars->surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 0, format); if (vars->surface == NULL) { vars->error = SDL_GetError(); return; } if (ckey != -1) { if (color_type != PNG_COLOR_TYPE_PALETTE) { /* FIXME: Should these be truncated or shifted down? */ ckey = SDL_MapRGB(vars->surface->format, (Uint8)transv->red, (Uint8)transv->green, (Uint8)transv->blue); } SDL_SetColorKey(vars->surface, SDL_TRUE, ckey); } /* Create the array of pointers to image data */ vars->row_pointers = (png_bytep*) SDL_malloc(sizeof(png_bytep)*height); if (!vars->row_pointers) { vars->error = "Out of memory"; return; } for (row = 0; row < (int)height; row++) { vars->row_pointers[row] = (png_bytep) (Uint8 *)vars->surface->pixels + row*vars->surface->pitch; } /* Read the entire image in one go */ lib.png_read_image(vars->png_ptr, vars->row_pointers); /* and we're done! (png_read_end() can be omitted if no processing of * post-IDAT text/time/etc. is desired) * In some cases it can't read PNG's created by some popular programs (ACDSEE), * we do not want to process comments, so we omit png_read_end lib.png_read_end(png_ptr, info_ptr); */ /* Load the palette, if any */ palette = vars->surface->format->palette; if ( palette ) { int png_num_palette; png_colorp png_palette; lib.png_get_PLTE(vars->png_ptr, vars->info_ptr, &png_palette, &png_num_palette); if (color_type == PNG_COLOR_TYPE_GRAY) { palette->ncolors = 256; for (i = 0; i < 256; i++) { palette->colors[i].r = (Uint8)i; palette->colors[i].g = (Uint8)i; palette->colors[i].b = (Uint8)i; } } else if (png_num_palette > 0 ) { palette->ncolors = png_num_palette; for ( i=0; icolors[i].b = png_palette[i].blue; palette->colors[i].g = png_palette[i].green; palette->colors[i].r = png_palette[i].red; } } } } SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src) { Sint64 start; struct loadpng_vars vars; if ( !src ) { /* The error message has been set in SDL_RWFromFile */ return NULL; } if ( (IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) == 0 ) { return NULL; } start = SDL_RWtell(src); SDL_zero(vars); LIBPNG_LoadPNG_RW(src, &vars); if (vars.png_ptr) { lib.png_destroy_read_struct(&vars.png_ptr, vars.info_ptr ? &vars.info_ptr : (png_infopp)0, (png_infopp)0); } if (vars.row_pointers) { SDL_free(vars.row_pointers); } if (vars.error) { SDL_RWseek(src, start, RW_SEEK_SET); if (vars.surface) { SDL_FreeSurface(vars.surface); vars.surface = NULL; } IMG_SetError("%s", vars.error); } return vars.surface; } #elif defined(USE_STBIMAGE) extern SDL_Surface *IMG_LoadSTB_RW(SDL_RWops *src); int IMG_InitPNG() { /* Nothing to load */ return 0; } void IMG_QuitPNG() { /* Nothing to unload */ } /* FIXME: This is a copypaste from LIBPNG! Pull that out of the ifdefs */ /* See if an image is contained in a data source */ int IMG_isPNG(SDL_RWops *src) { Sint64 start; int is_PNG; Uint8 magic[4]; if ( !src ) { return 0; } start = SDL_RWtell(src); is_PNG = 0; if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) { if ( magic[0] == 0x89 && magic[1] == 'P' && magic[2] == 'N' && magic[3] == 'G' ) { is_PNG = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_PNG); } /* Load a PNG type image from an SDL datasource */ SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src) { return IMG_LoadSTB_RW(src); } #endif /* WANT_LIBPNG */ #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif int IMG_InitPNG() { IMG_SetError("PNG images are not supported"); return(-1); } void IMG_QuitPNG() { } /* See if an image is contained in a data source */ int IMG_isPNG(SDL_RWops *src) { return(0); } /* Load a PNG type image from an SDL datasource */ SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_PNG */ #if SDL_IMAGE_SAVE_PNG static const Uint32 png_format = SDL_PIXELFORMAT_RGBA32; #ifdef USE_LIBPNG static void png_write_data(png_structp png_ptr, png_bytep src, png_size_t size) { SDL_RWops *dst = (SDL_RWops *)lib.png_get_io_ptr(png_ptr); SDL_RWwrite(dst, src, size, 1); } static void png_flush_data(png_structp png_ptr) { (void)png_ptr; } struct savepng_vars { png_structp png_ptr; png_infop info_ptr; const char *error; png_colorp color_ptr; png_bytep *row_pointers; SDL_Surface *source; }; static int LIBPNG_SavePNG_RW(struct savepng_vars *vars, SDL_Surface *surface, SDL_RWops *dst) { Uint8 transparent_table[256]; SDL_Palette *palette; int png_color_type; vars->source = surface; vars->png_ptr = lib.png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (vars->png_ptr == NULL) { return IMG_SetError("Couldn't allocate memory for PNG file or incompatible PNG dll"); } vars->info_ptr = lib.png_create_info_struct(vars->png_ptr); if (vars->info_ptr == NULL) { vars->error = "Couldn't create image information for PNG file"; return -1; } #ifdef PNG_SETJMP_SUPPORTED #ifndef LIBPNG_VERSION_12 if (setjmp(*lib.png_set_longjmp_fn(vars->png_ptr, longjmp, sizeof (jmp_buf)))) #else if (setjmp(vars->png_ptr->jmpbuf)) #endif #endif { vars->error = "Error writing the PNG file."; return -1; } palette = surface->format->palette; if (palette) { const int ncolors = palette->ncolors; int i; int last_transparent = -1; vars->color_ptr = (png_colorp)SDL_malloc(sizeof(png_color) * ncolors); if (vars->color_ptr == NULL) { vars->error = "Couldn't create palette for PNG file"; return -1; } for (i = 0; i < ncolors; i++) { vars->color_ptr[i].red = palette->colors[i].r; vars->color_ptr[i].green = palette->colors[i].g; vars->color_ptr[i].blue = palette->colors[i].b; if (palette->colors[i].a != 255) { last_transparent = i; } } lib.png_set_PLTE(vars->png_ptr, vars->info_ptr, vars->color_ptr, ncolors); png_color_type = PNG_COLOR_TYPE_PALETTE; if (last_transparent >= 0) { for (i = 0; i <= last_transparent; ++i) { transparent_table[i] = palette->colors[i].a; } lib.png_set_tRNS(vars->png_ptr, vars->info_ptr, transparent_table, last_transparent + 1, NULL); } } else if (surface->format->format == SDL_PIXELFORMAT_RGB24) { /* If the surface is exactly the right RGB format it is just passed through */ png_color_type = PNG_COLOR_TYPE_RGB; } else if (!SDL_ISPIXELFORMAT_ALPHA(surface->format->format)) { /* If the surface is not exactly the right RGB format but does not have alpha information, it should be converted to RGB24 before being passed through */ png_color_type = PNG_COLOR_TYPE_RGB; vars->source = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGB24, 0); } else if (surface->format->format != png_format) { /* Otherwise, (surface has alpha data), and it is not in the exact right format , so it should be converted to that */ png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; vars->source = SDL_ConvertSurfaceFormat(surface, png_format, 0); } else { png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; } lib.png_set_write_fn(vars->png_ptr, dst, png_write_data, png_flush_data); lib.png_set_IHDR(vars->png_ptr, vars->info_ptr, surface->w, surface->h, 8, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); if (vars->source) { int row; vars->row_pointers = (png_bytep *) SDL_malloc(sizeof(png_bytep) * vars->source->h); if (!vars->row_pointers) { vars->error = "Out of memory"; return -1; } for (row = 0; row < (int)vars->source->h; row++) { vars->row_pointers[row] = (png_bytep) (Uint8 *) vars->source->pixels + row * vars->source->pitch; } lib.png_set_rows(vars->png_ptr, vars->info_ptr, vars->row_pointers); lib.png_write_png(vars->png_ptr, vars->info_ptr, PNG_TRANSFORM_IDENTITY, NULL); } return 0; } static int IMG_SavePNG_RW_libpng(SDL_Surface *surface, SDL_RWops *dst) { struct savepng_vars vars; int ret; if (!IMG_Init(IMG_INIT_PNG)) { return -1; } SDL_zero(vars); ret = LIBPNG_SavePNG_RW(&vars, surface, dst); if (vars.png_ptr) { lib.png_destroy_write_struct(&vars.png_ptr, &vars.info_ptr); } if (vars.color_ptr) { SDL_free(vars.color_ptr); } if (vars.row_pointers) { SDL_free(vars.row_pointers); } if (vars.source != surface) { SDL_FreeSurface(vars.source); } if (vars.error) { IMG_SetError("%s", vars.error); } return ret; } #endif /* USE_LIBPNG */ #if defined(LOAD_PNG_DYNAMIC) || !defined(WANT_LIBPNG) /* Replace C runtime functions with SDL C runtime functions for building on Windows */ #define MINIZ_NO_STDIO #define MINIZ_NO_TIME #define MINIZ_SDL_MALLOC #define MZ_ASSERT(x) SDL_assert(x) #undef memcpy #define memcpy SDL_memcpy #undef memset #define memset SDL_memset #define strlen SDL_strlen #if SDL_BYTEORDER == SDL_LIL_ENDIAN #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #define MINIZ_SDL_NOUNUSED #include "miniz.h" static int IMG_SavePNG_RW_miniz(SDL_Surface *surface, SDL_RWops *dst) { size_t size = 0; void *png = NULL; int result = -1; if (!dst) { return IMG_SetError("Passed NULL dst"); } if (surface->format->format == png_format) { png = tdefl_write_image_to_png_file_in_memory(surface->pixels, surface->w, surface->h, surface->format->BytesPerPixel, surface->pitch, &size); } else { SDL_Surface *cvt = SDL_ConvertSurfaceFormat(surface, png_format, 0); if (cvt) { png = tdefl_write_image_to_png_file_in_memory(cvt->pixels, cvt->w, cvt->h, cvt->format->BytesPerPixel, cvt->pitch, &size); SDL_FreeSurface(cvt); } } if (png) { if (SDL_RWwrite(dst, png, size, 1)) { result = 0; } mz_free(png); /* calls SDL_free() */ } else { return IMG_SetError("Failed to convert and save image"); } return result; } #endif /* LOAD_PNG_DYNAMIC || !WANT_LIBPNG */ #endif /* SDL_IMAGE_SAVE_PNG */ int IMG_SavePNG(SDL_Surface *surface, const char *file) { SDL_RWops *dst = SDL_RWFromFile(file, "wb"); if (dst) { return IMG_SavePNG_RW(surface, dst, 1); } else { return -1; } } int IMG_SavePNG_RW(SDL_Surface *surface, SDL_RWops *dst, int freedst) { int result = -1; if (!dst) { return IMG_SetError("Passed NULL dst"); } #if SDL_IMAGE_SAVE_PNG #ifdef USE_LIBPNG if (result < 0) { result = IMG_SavePNG_RW_libpng(surface, dst); } #endif #if defined(LOAD_PNG_DYNAMIC) || !defined(WANT_LIBPNG) if (result < 0) { result = IMG_SavePNG_RW_miniz(surface, dst); } #endif #else result = IMG_SetError("SDL_image built without PNG save support"); #endif if (freedst) { SDL_RWclose(dst); } return result; } SDL2_image-2.8.8/src/IMG_pnm.c0000664000000000000000000001621114751445211012555 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* * PNM (portable anymap) image loader: * * Supports: PBM, PGM and PPM, ASCII and binary formats * (PBM and PGM are loaded as 8bpp surfaces) * Does not support: maximum component value > 255 */ #include "SDL_image.h" #ifdef LOAD_PNM /* See if an image is contained in a data source */ int IMG_isPNM(SDL_RWops *src) { Sint64 start; int is_PNM; char magic[2]; if ( !src ) return 0; start = SDL_RWtell(src); is_PNM = 0; if ( SDL_RWread(src, magic, sizeof(magic), 1) ) { /* * PNM magic signatures: * P1 PBM, ascii format * P2 PGM, ascii format * P3 PPM, ascii format * P4 PBM, binary format * P5 PGM, binary format * P6 PPM, binary format * P7 PAM, a general wrapper for PNM data */ if ( magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6' ) { is_PNM = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_PNM); } /* read a non-negative integer from the source. return -1 upon error */ static int ReadNumber(SDL_RWops *src) { int number; unsigned char ch; /* Initialize return value */ number = 0; /* Skip leading whitespace */ do { if ( ! SDL_RWread(src, &ch, 1, 1) ) { return(-1); } /* Eat comments as whitespace */ if ( ch == '#' ) { /* Comment is '#' to end of line */ do { if ( ! SDL_RWread(src, &ch, 1, 1) ) { return -1; } } while ( (ch != '\r') && (ch != '\n') ); } } while ( SDL_isspace(ch) ); /* Add up the number */ if (!SDL_isdigit(ch)) { return -1; } do { /* Protect from possible overflow */ if (number >= (SDL_MAX_SINT32 / 10)) { return -1; } number *= 10; number += ch-'0'; if ( !SDL_RWread(src, &ch, 1, 1) ) { return -1; } } while ( SDL_isdigit(ch) ); return(number); } SDL_Surface *IMG_LoadPNM_RW(SDL_RWops *src) { Sint64 start; SDL_Surface *surface = NULL; int width, height; int maxval, y, bpl; Uint8 *row; Uint8 *buf = NULL; char *error = NULL; Uint8 magic[2]; int ascii; enum { PBM, PGM, PPM, PAM } kind; #define ERROR(s) do { error = (s); goto done; } while(0) if ( !src ) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); SDL_RWread(src, magic, 2, 1); kind = magic[1] - '1'; ascii = 1; if(kind >= 3) { ascii = 0; kind -= 3; } width = ReadNumber(src); height = ReadNumber(src); if(width <= 0 || height <= 0) ERROR("Unable to read image width and height"); if(kind != PBM) { maxval = ReadNumber(src); if(maxval <= 0 || maxval > 255) ERROR("unsupported PNM format"); } else maxval = 255; /* never scale PBMs */ /* binary PNM allows just a single character of whitespace after the last parameter, and we've already consumed it */ if(kind == PPM) { /* 24-bit surface in R,G,B byte order */ surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 0, SDL_PIXELFORMAT_RGB24); } else { /* load PBM/PGM as 8-bit indexed images */ surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 0, SDL_PIXELFORMAT_INDEX8); } if ( surface == NULL ) ERROR("Out of memory"); bpl = width * surface->format->BytesPerPixel; if(kind == PGM) { SDL_Color *c = surface->format->palette->colors; int i; for(i = 0; i < 256; i++) c[i].r = c[i].g = c[i].b = i; surface->format->palette->ncolors = 256; } else if(kind == PBM) { /* for some reason PBM has 1=black, 0=white */ SDL_Color *c = surface->format->palette->colors; c[0].r = c[0].g = c[0].b = 255; c[1].r = c[1].g = c[1].b = 0; surface->format->palette->ncolors = 2; bpl = (width + 7) >> 3; buf = (Uint8 *)SDL_malloc(bpl); if(buf == NULL) ERROR("Out of memory"); } /* Read the image into the surface */ row = (Uint8 *)surface->pixels; for(y = 0; y < height; y++) { if(ascii) { int i; if(kind == PBM) { for(i = 0; i < width; i++) { Uint8 ch; do { if(!SDL_RWread(src, &ch, 1, 1)) ERROR("file truncated"); ch -= '0'; } while(ch > 1); row[i] = ch; } } else { for(i = 0; i < bpl; i++) { int c; c = ReadNumber(src); if(c < 0) ERROR("file truncated"); row[i] = c; } } } else { Uint8 *dst = (kind == PBM) ? buf : row; if(!SDL_RWread(src, dst, bpl, 1)) ERROR("file truncated"); if(kind == PBM) { /* expand bitmap to 8bpp */ int i; for(i = 0; i < width; i++) { int bit = 7 - (i & 7); row[i] = (buf[i >> 3] >> bit) & 1; } } } if(maxval < 255) { /* scale up to full dynamic range (slow) */ int i; for(i = 0; i < bpl; i++) row[i] = row[i] * 255 / maxval; } row += surface->pitch; } done: SDL_free(buf); if(error) { SDL_RWseek(src, start, RW_SEEK_SET); if ( surface ) { SDL_FreeSurface(surface); surface = NULL; } IMG_SetError("%s", error); } return(surface); } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif /* See if an image is contained in a data source */ int IMG_isPNM(SDL_RWops *src) { return(0); } /* Load a PNM type image from an SDL datasource */ SDL_Surface *IMG_LoadPNM_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_PNM */ SDL2_image-2.8.8/src/IMG_qoi.c0000664000000000000000000000671514751445211012563 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This file use QOI library: * https://github.com/phoboslab/qoi */ #include "SDL_image.h" #include /* for INT_MAX */ #ifdef LOAD_QOI /* SDL < 2.0.12 compatibility */ #ifndef SDL_zeroa #define SDL_zeroa(x) SDL_memset((x), 0, sizeof((x))) #endif #define QOI_MALLOC(sz) SDL_malloc(sz) #define QOI_FREE(p) SDL_free(p) #define QOI_ZEROARR(a) SDL_zeroa(a) #define QOI_NO_STDIO #define QOI_IMPLEMENTATION #include "qoi.h" /* See if an image is contained in a data source */ int IMG_isQOI(SDL_RWops *src) { Sint64 start; int is_QOI; char magic[4]; if ( !src ) return 0; start = SDL_RWtell(src); is_QOI = 0; if ( SDL_RWread(src, magic, sizeof(magic), 1) ) { if ( SDL_strncmp(magic, "qoif", 4) == 0 ) { is_QOI = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_QOI); } /* Load a QOI type image from an SDL datasource */ SDL_Surface *IMG_LoadQOI_RW(SDL_RWops *src) { void *data; size_t size; void *pixel_data; qoi_desc image_info; SDL_Surface *surface = NULL; data = (void *)SDL_LoadFile_RW(src, &size, SDL_FALSE); if ( !data ) { return NULL; } if ( size > INT_MAX ) { SDL_free(data); IMG_SetError("QOI image is too big."); return NULL; } pixel_data = qoi_decode(data, (int)size, &image_info, 4); /* pixel_data is in R,G,B,A order regardless of endianness */ SDL_free(data); if ( !pixel_data ) { IMG_SetError("Couldn't parse QOI image"); return NULL; } surface = SDL_CreateRGBSurfaceWithFormatFrom(pixel_data, image_info.width, image_info.height, 32, (image_info.width * 4), SDL_PIXELFORMAT_RGBA32); if ( !surface ) { QOI_FREE(pixel_data); IMG_SetError("Couldn't create SDL_Surface"); return NULL; } /* Let SDL manage the memory now */ surface->flags &= ~SDL_PREALLOC; return surface; } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif /* See if an image is contained in a data source */ int IMG_isQOI(SDL_RWops *src) { return(0); } /* Load a QOI type image from an SDL datasource */ SDL_Surface *IMG_LoadQOI_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_QOI */ SDL2_image-2.8.8/src/IMG_stb.c0000664000000000000000000002030314751445211012550 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #include "SDL_image.h" #ifdef USE_STBIMAGE #define malloc SDL_malloc #define realloc SDL_realloc #define free SDL_free #undef memcpy #define memcpy SDL_memcpy #undef memset #define memset SDL_memset #undef strcmp #define strcmp SDL_strcmp #undef strncmp #define strncmp SDL_strncmp #define strtol SDL_strtol #define pow SDL_pow #define ldexp SDL_scalbn #define STB_IMAGE_STATIC #define STBI_NO_THREAD_LOCALS #define STBI_FAILURE_USERMSG #if defined(__ARM_NEON) #define STBI_NEON #endif #define STBI_NO_STDIO #define STBI_ONLY_PNG #define STBI_ONLY_JPEG #define STBI_NO_GIF #define STBI_NO_HDR #define STBI_NO_LINEAR #define STBI_ASSERT SDL_assert #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" static int IMG_LoadSTB_RW_read(void *user, char *data, int size) { return (int) SDL_RWread((SDL_RWops*)user, data, 1, size); } static void IMG_LoadSTB_RW_skip(void *user, int n) { SDL_RWseek((SDL_RWops*)user, n, RW_SEEK_CUR); } static int IMG_LoadSTB_RW_eof(void *user) { /* FIXME: Do we not have a way to detect EOF? -flibit */ size_t bytes, filler; SDL_RWops *src = (SDL_RWops*)user; bytes = SDL_RWread(src, &filler, 1, 1); if (bytes != 1) { /* FIXME: Could also be an error... */ return 1; } SDL_RWseek(src, -1, RW_SEEK_CUR); return 0; } SDL_Surface *IMG_LoadSTB_RW(SDL_RWops *src) { Sint64 start; Uint8 magic[26]; int w, h, format; stbi_uc *pixels; stbi_io_callbacks rw_callbacks; SDL_Surface *surface = NULL; SDL_bool use_palette = SDL_FALSE; unsigned int palette_colors[256]; if (!src) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); if (SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic)) { const Uint8 PNG_COLOR_INDEXED = 3; if (magic[0] == 0x89 && magic[1] == 'P' && magic[2] == 'N' && magic[3] == 'G' && magic[12] == 'I' && magic[13] == 'H' && magic[14] == 'D' && magic[15] == 'R' && magic[25] == PNG_COLOR_INDEXED) { use_palette = SDL_TRUE; } } SDL_RWseek(src, start, RW_SEEK_SET); /* Load the image data */ rw_callbacks.read = IMG_LoadSTB_RW_read; rw_callbacks.skip = IMG_LoadSTB_RW_skip; rw_callbacks.eof = IMG_LoadSTB_RW_eof; w = h = format = 0; /* silence warning */ if (use_palette) { /* Unused palette entries will be opaque white */ SDL_memset(palette_colors, 0xff, sizeof(palette_colors)); pixels = stbi_load_from_callbacks_with_palette( &rw_callbacks, src, &w, &h, palette_colors, SDL_arraysize(palette_colors) ); } else { pixels = stbi_load_from_callbacks( &rw_callbacks, src, &w, &h, &format, STBI_default ); } if (!pixels) { SDL_RWseek(src, start, RW_SEEK_SET); return NULL; } if (use_palette) { surface = SDL_CreateRGBSurfaceWithFormatFrom( pixels, w, h, 8, w, SDL_PIXELFORMAT_INDEX8 ); if (surface) { SDL_bool has_colorkey = SDL_FALSE; int colorkey_index = -1; SDL_bool has_alpha = SDL_FALSE; SDL_Palette *palette = surface->format->palette; if (palette) { int i; Uint8 *palette_bytes = (Uint8 *)palette_colors; for (i = 0; i < palette->ncolors; i++) { palette->colors[i].r = *palette_bytes++; palette->colors[i].g = *palette_bytes++; palette->colors[i].b = *palette_bytes++; palette->colors[i].a = *palette_bytes++; if (palette->colors[i].a != SDL_ALPHA_OPAQUE) { if (palette->colors[i].a == SDL_ALPHA_TRANSPARENT && !has_colorkey) { has_colorkey = SDL_TRUE; colorkey_index = i; } else { /* Partial opacity or multiple colorkeys */ has_alpha = SDL_TRUE; } } } } if (has_alpha) { /* SDL doesn't support blitting with the palette alpha, so expand the palette */ SDL_Surface *converted = SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA32, 0); SDL_FreeSurface(surface); surface = converted; } else if (has_colorkey) { SDL_SetColorKey(surface, SDL_TRUE, colorkey_index); } /* FIXME: This sucks. It'd be better to allocate the surface first, then * write directly to the pixel buffer: * https://github.com/nothings/stb/issues/58 * -flibit */ if (surface) { surface->flags &= ~SDL_PREALLOC; } } } else if (format == STBI_grey || format == STBI_rgb || format == STBI_rgb_alpha) { surface = SDL_CreateRGBSurfaceWithFormatFrom( pixels, w, h, 8 * format, w * format, (format == STBI_rgb_alpha) ? SDL_PIXELFORMAT_RGBA32 : (format == STBI_rgb) ? SDL_PIXELFORMAT_RGB24 : SDL_PIXELFORMAT_INDEX8 ); if (surface) { /* Set a grayscale palette for gray images */ SDL_Palette *palette = surface->format->palette; if (palette) { int i; for (i = 0; i < palette->ncolors; i++) { palette->colors[i].r = (Uint8)i; palette->colors[i].g = (Uint8)i; palette->colors[i].b = (Uint8)i; } } /* FIXME: This sucks. It'd be better to allocate the surface first, then * write directly to the pixel buffer: * https://github.com/nothings/stb/issues/58 * -flibit */ surface->flags &= ~SDL_PREALLOC; } } else if (format == STBI_grey_alpha) { surface = SDL_CreateRGBSurfaceWithFormat( 0, w, h, 32, SDL_PIXELFORMAT_RGBA32 ); if (surface) { Uint8 *src = pixels; Uint8 *dst = (Uint8 *)surface->pixels; int skip = surface->pitch - (surface->w * 4); int row, col; for (row = 0; row < h; ++row) { for (col = 0; col < w; ++col) { Uint8 c = *src++; Uint8 a = *src++; *dst++ = c; *dst++ = c; *dst++ = c; *dst++ = a; } dst += skip; } stbi_image_free(pixels); } } else { IMG_SetError("Unknown image format: %d", format); } if (!surface) { /* The error message should already be set */ stbi_image_free(pixels); /* calls SDL_free() */ SDL_RWseek(src, start, RW_SEEK_SET); } return surface; } #endif /* USE_STBIMAGE */ SDL2_image-2.8.8/src/IMG_svg.c0000664000000000000000000001237614751445211012572 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This is an SVG image file loading framework, based on Nano SVG: * https://github.com/memononen/nanosvg */ #include "SDL_image.h" #ifdef LOAD_SVG #if !SDL_VERSION_ATLEAST(2, 0, 16) /* SDL_roundf() is available starting with 2.0.16 */ static float SDLCALL SDL_roundf(float x) { return (x >= 0.0f) ? SDL_floorf(x + 0.5f) : SDL_ceilf(x - 0.5f); } #endif /* SDL 2.0.16 */ /* Replace C runtime functions with SDL C runtime functions for building on Windows */ #define free SDL_free #define malloc SDL_malloc #undef memcpy #define memcpy SDL_memcpy #undef memset #define memset SDL_memset #define qsort SDL_qsort #define realloc SDL_realloc #define sscanf SDL_sscanf #undef strchr #define strchr SDL_strchr #undef strcmp #define strcmp SDL_strcmp #undef strncmp #define strncmp SDL_strncmp #undef strncpy #define strncpy SDL_strlcpy #define strlen SDL_strlen #define strstr SDL_strstr #define strtol SDL_strtol #define strtoll SDL_strtoll #define acosf SDL_acosf #define atan2f SDL_atan2f #define cosf SDL_cosf #define ceilf SDL_ceilf #define fabs SDL_fabs #define fabsf SDL_fabsf #define floorf SDL_floorf #define fmodf SDL_fmodf #define pow SDL_pow #define sinf SDL_sinf #define sqrt SDL_sqrt #define sqrtf SDL_sqrtf #define tanf SDL_tanf #define roundf SDL_roundf #ifndef FLT_MAX #define FLT_MAX 3.402823466e+38F #endif #undef HAVE_STDIO_H #define NSVG_EXPORT static #define NANOSVG_IMPLEMENTATION #include "nanosvg.h" #define NANOSVGRAST_IMPLEMENTATION #include "nanosvgrast.h" /* See if an image is contained in a data source */ int IMG_isSVG(SDL_RWops *src) { Sint64 start; int is_SVG; char magic[4096]; size_t magic_len; if (!src) return 0; start = SDL_RWtell(src); is_SVG = 0; magic_len = SDL_RWread(src, magic, 1, sizeof(magic) - 1); magic[magic_len] = '\0'; if (SDL_strstr(magic, "width <= 0.0f || image->height <= 0.0f) { IMG_SetError("Couldn't parse SVG image"); return NULL; } rasterizer = nsvgCreateRasterizer(); if (!rasterizer) { IMG_SetError("Couldn't create SVG rasterizer"); nsvgDelete(image); return NULL; } if (width > 0 && height > 0) { float scale_x = (float)width / image->width; float scale_y = (float)height / image->height; scale = SDL_min(scale_x, scale_y); } else if (width > 0) { scale = (float)width / image->width; } else if (height > 0) { scale = (float)height / image->height; } else { scale = 1.0f; } surface = SDL_CreateRGBSurfaceWithFormat(0, (int)SDL_ceilf(image->width * scale), (int)SDL_ceilf(image->height * scale), 32, SDL_PIXELFORMAT_RGBA32); if (!surface) { nsvgDeleteRasterizer(rasterizer); nsvgDelete(image); return NULL; } nsvgRasterize(rasterizer, image, 0.0f, 0.0f, scale, (unsigned char *)surface->pixels, surface->w, surface->h, surface->pitch); nsvgDeleteRasterizer(rasterizer); nsvgDelete(image); return surface; } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif /* See if an image is contained in a data source */ int IMG_isSVG(SDL_RWops *src) { return(0); } /* Load a SVG type image from an SDL datasource */ SDL_Surface *IMG_LoadSizedSVG_RW(SDL_RWops *src, int width, int height) { return(NULL); } #endif /* LOAD_SVG */ /* Load a SVG type image from an SDL datasource */ SDL_Surface *IMG_LoadSVG_RW(SDL_RWops *src) { return IMG_LoadSizedSVG_RW(src, 0, 0); } SDL2_image-2.8.8/src/IMG_tga.c0000664000000000000000000002137214751445211012542 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #if !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) /* This is a Targa image file loading framework */ #include "SDL_endian.h" #include "SDL_image.h" #ifdef LOAD_TGA /* * A TGA loader for the SDL library * Supports: Reading 8, 15, 16, 24 and 32bpp images, with alpha or colourkey, * uncompressed or RLE encoded. * * 2000-06-10 Mattias Engdegхrd : initial version * 2000-06-26 Mattias Engdegхrd : read greyscale TGAs * 2000-08-09 Mattias Engdegхrd : alpha inversion removed */ struct TGAheader { Uint8 infolen; /* length of info field */ Uint8 has_cmap; /* 1 if image has colormap, 0 otherwise */ Uint8 type; Uint8 cmap_start[2]; /* index of first colormap entry */ Uint8 cmap_len[2]; /* number of entries in colormap */ Uint8 cmap_bits; /* bits per colormap entry */ Uint8 yorigin[2]; /* image origin (ignored here) */ Uint8 xorigin[2]; Uint8 width[2]; /* image size */ Uint8 height[2]; Uint8 pixel_bits; /* bits/pixel */ Uint8 flags; }; enum tga_type { TGA_TYPE_INDEXED = 1, TGA_TYPE_RGB = 2, TGA_TYPE_BW = 3, TGA_TYPE_RLE_INDEXED = 9, TGA_TYPE_RLE_RGB = 10, TGA_TYPE_RLE_BW = 11 }; #define TGA_INTERLEAVE_MASK 0xc0 #define TGA_INTERLEAVE_NONE 0x00 #define TGA_INTERLEAVE_2WAY 0x40 #define TGA_INTERLEAVE_4WAY 0x80 #define TGA_ORIGIN_MASK 0x30 #define TGA_ORIGIN_LEFT 0x00 #define TGA_ORIGIN_RIGHT 0x10 #define TGA_ORIGIN_LOWER 0x00 #define TGA_ORIGIN_UPPER 0x20 /* read/write unaligned little-endian 16-bit ints */ #define LE16(p) ((p)[0] + ((p)[1] << 8)) #define SETLE16(p, v) ((p)[0] = (v), (p)[1] = (v) >> 8) /* Load a TGA type image from an SDL datasource */ SDL_Surface *IMG_LoadTGA_RW(SDL_RWops *src) { Sint64 start; const char *error = NULL; struct TGAheader hdr; int rle = 0; int indexed = 0; int grey = 0; int ckey = -1; int ncols, w, h; SDL_Surface *img = NULL; Uint32 format; Uint8 *dst; int i; int bpp; int lstep; Uint32 pixel; int count, rep; if ( !src ) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); if (!SDL_RWread(src, &hdr, sizeof(hdr), 1)) { error = "Error reading TGA data"; goto error; } ncols = LE16(hdr.cmap_len); switch(hdr.type) { case TGA_TYPE_RLE_INDEXED: rle = 1; /* fallthrough */ case TGA_TYPE_INDEXED: if (!hdr.has_cmap || hdr.pixel_bits != 8 || ncols > 256) goto unsupported; indexed = 1; break; case TGA_TYPE_RLE_RGB: rle = 1; /* fallthrough */ case TGA_TYPE_RGB: indexed = 0; break; case TGA_TYPE_RLE_BW: rle = 1; /* fallthrough */ case TGA_TYPE_BW: if (hdr.pixel_bits != 8) goto unsupported; /* Treat greyscale as 8bpp indexed images */ indexed = grey = 1; break; default: goto unsupported; } bpp = (hdr.pixel_bits + 7) >> 3; switch(hdr.pixel_bits) { case 8: if (!indexed) { goto unsupported; } format = SDL_PIXELFORMAT_INDEX8; break; case 15: case 16: /* 15 and 16bpp both seem to use 5 bits/plane. The extra alpha bit is ignored for now. */ format = SDL_PIXELFORMAT_RGB555; break; case 32: format = SDL_PIXELFORMAT_BGRA32; break; case 24: format = SDL_PIXELFORMAT_BGR24; break; default: goto unsupported; } if ((hdr.flags & TGA_INTERLEAVE_MASK) != TGA_INTERLEAVE_NONE || hdr.flags & TGA_ORIGIN_RIGHT) { goto unsupported; } SDL_RWseek(src, hdr.infolen, RW_SEEK_CUR); /* skip info field */ w = LE16(hdr.width); h = LE16(hdr.height); img = SDL_CreateRGBSurfaceWithFormat(0, w, h, 0, format); if (img == NULL) { error = "Out of memory"; goto error; } if (hdr.has_cmap) { int palsiz = ncols * ((hdr.cmap_bits + 7) >> 3); if (indexed && !grey) { Uint8 *pal = (Uint8 *)SDL_malloc(palsiz), *p = pal; SDL_Color *colors = img->format->palette->colors; img->format->palette->ncolors = ncols; SDL_RWread(src, pal, palsiz, 1); for(i = 0; i < ncols; i++) { switch(hdr.cmap_bits) { case 15: case 16: { Uint16 c = p[0] + (p[1] << 8); p += 2; colors[i].r = (c >> 7) & 0xf8; colors[i].g = (c >> 2) & 0xf8; colors[i].b = c << 3; } break; case 24: case 32: colors[i].b = *p++; colors[i].g = *p++; colors[i].r = *p++; if (hdr.cmap_bits == 32 && *p++ < 128) ckey = i; break; } } SDL_free(pal); if (ckey >= 0) SDL_SetColorKey(img, SDL_TRUE, ckey); } else { /* skip unneeded colormap */ SDL_RWseek(src, palsiz, RW_SEEK_CUR); } } if (grey) { SDL_Color *colors = img->format->palette->colors; for(i = 0; i < 256; i++) colors[i].r = colors[i].g = colors[i].b = i; img->format->palette->ncolors = 256; } if (hdr.flags & TGA_ORIGIN_UPPER) { lstep = img->pitch; dst = (Uint8 *)img->pixels; } else { lstep = -img->pitch; dst = (Uint8 *)img->pixels + (h - 1) * img->pitch; } /* The RLE decoding code is slightly convoluted since we can't rely on spans not to wrap across scan lines */ count = rep = 0; for(i = 0; i < h; i++) { if (rle) { int x = 0; for(;;) { Uint8 c; if (count) { int n = count; if (n > w - x) n = w - x; SDL_RWread(src, dst + x * bpp, n * bpp, 1); count -= n; x += n; if (x == w) break; } else if (rep) { int n = rep; if (n > w - x) n = w - x; rep -= n; while (n--) { SDL_memcpy(dst + x * bpp, &pixel, bpp); x++; } if (x == w) break; } SDL_RWread(src, &c, 1, 1); if (c & 0x80) { SDL_RWread(src, &pixel, bpp, 1); rep = (c & 0x7f) + 1; } else { count = c + 1; } } } else { SDL_RWread(src, dst, w * bpp, 1); } #if SDL_BYTEORDER == SDL_BIG_ENDIAN if (bpp == 2) { /* swap byte order */ int x; Uint16 *p = (Uint16 *)dst; for(x = 0; x < w; x++) p[x] = SDL_Swap16(p[x]); } #endif dst += lstep; } return img; unsupported: error = "Unsupported TGA format"; error: SDL_RWseek(src, start, RW_SEEK_SET); if ( img ) { SDL_FreeSurface(img); } IMG_SetError("%s", error); return NULL; } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif /* dummy TGA load routine */ SDL_Surface *IMG_LoadTGA_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_TGA */ #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */ SDL2_image-2.8.8/src/IMG_tif.c0000664000000000000000000001463414751445211012554 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #if !(defined(__APPLE__) || defined(SDL_IMAGE_USE_WIC_BACKEND)) || defined(SDL_IMAGE_USE_COMMON_BACKEND) /* This is a TIFF image file loading framework */ #include "SDL_image.h" #ifdef LOAD_TIF #include static struct { int loaded; void *handle; TIFF* (*TIFFClientOpen)(const char*, const char*, thandle_t, TIFFReadWriteProc, TIFFReadWriteProc, TIFFSeekProc, TIFFCloseProc, TIFFSizeProc, TIFFMapFileProc, TIFFUnmapFileProc); void (*TIFFClose)(TIFF*); int (*TIFFGetField)(TIFF*, ttag_t, ...); int (*TIFFReadRGBAImageOriented)(TIFF*, Uint32, Uint32, Uint32*, int, int); TIFFErrorHandler (*TIFFSetErrorHandler)(TIFFErrorHandler); } lib; #ifdef LOAD_TIF_DYNAMIC #define FUNCTION_LOADER(FUNC, SIG) \ lib.FUNC = (SIG) SDL_LoadFunction(lib.handle, #FUNC); \ if (lib.FUNC == NULL) { SDL_UnloadObject(lib.handle); return -1; } #else #define FUNCTION_LOADER(FUNC, SIG) \ lib.FUNC = FUNC; #endif int IMG_InitTIF() { if ( lib.loaded == 0 ) { #ifdef LOAD_TIF_DYNAMIC lib.handle = SDL_LoadObject(LOAD_TIF_DYNAMIC); if ( lib.handle == NULL ) { return -1; } #endif FUNCTION_LOADER(TIFFClientOpen, TIFF * (*)(const char*, const char*, thandle_t, TIFFReadWriteProc, TIFFReadWriteProc, TIFFSeekProc, TIFFCloseProc, TIFFSizeProc, TIFFMapFileProc, TIFFUnmapFileProc)) FUNCTION_LOADER(TIFFClose, void (*)(TIFF*)) FUNCTION_LOADER(TIFFGetField, int (*)(TIFF*, ttag_t, ...)) FUNCTION_LOADER(TIFFReadRGBAImageOriented, int (*)(TIFF*, Uint32, Uint32, Uint32*, int, int)) FUNCTION_LOADER(TIFFSetErrorHandler, TIFFErrorHandler (*)(TIFFErrorHandler)) } ++lib.loaded; return 0; } void IMG_QuitTIF() { if ( lib.loaded == 0 ) { return; } if ( lib.loaded == 1 ) { #ifdef LOAD_TIF_DYNAMIC SDL_UnloadObject(lib.handle); #endif } --lib.loaded; } /* * These are the thunking routine to use the SDL_RWops* routines from * libtiff's internals. */ static tsize_t tiff_read(thandle_t fd, tdata_t buf, tsize_t size) { return (tsize_t)SDL_RWread((SDL_RWops*)fd, buf, 1, size); } static toff_t tiff_seek(thandle_t fd, toff_t offset, int origin) { return SDL_RWseek((SDL_RWops*)fd, offset, origin); } static tsize_t tiff_write(thandle_t fd, tdata_t buf, tsize_t size) { return (tsize_t)SDL_RWwrite((SDL_RWops*)fd, buf, 1, size); } static int tiff_close(thandle_t fd) { /* * We don't want libtiff closing our SDL_RWops*, but if it's not given * a routine to try, and if the image isn't a TIFF, it'll segfault. */ return 0; } static int tiff_map(thandle_t fd, tdata_t* pbase, toff_t* psize) { return (0); } static void tiff_unmap(thandle_t fd, tdata_t base, toff_t size) { return; } static toff_t tiff_size(thandle_t fd) { Sint64 save_pos; toff_t size; save_pos = SDL_RWtell((SDL_RWops*)fd); SDL_RWseek((SDL_RWops*)fd, 0, RW_SEEK_END); size = SDL_RWtell((SDL_RWops*)fd); SDL_RWseek((SDL_RWops*)fd, save_pos, RW_SEEK_SET); return size; } int IMG_isTIF(SDL_RWops* src) { Sint64 start; int is_TIF; Uint8 magic[4]; if ( !src ) return 0; start = SDL_RWtell(src); is_TIF = 0; if ( SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic) ) { if ( (magic[0] == 'I' && magic[1] == 'I' && magic[2] == 0x2a && magic[3] == 0x00) || (magic[0] == 'M' && magic[1] == 'M' && magic[2] == 0x00 && magic[3] == 0x2a) ) { is_TIF = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_TIF); } SDL_Surface* IMG_LoadTIF_RW(SDL_RWops* src) { Sint64 start; TIFF* tiff = NULL; SDL_Surface* surface = NULL; Uint32 img_width, img_height; if ( !src ) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); if ( (IMG_Init(IMG_INIT_TIF) & IMG_INIT_TIF) == 0 ) { return NULL; } /* turn off memory mapped access with the m flag */ tiff = lib.TIFFClientOpen("SDL_image", "rm", (thandle_t)src, tiff_read, tiff_write, tiff_seek, tiff_close, tiff_size, tiff_map, tiff_unmap); if(!tiff) goto error; /* Retrieve the dimensions of the image from the TIFF tags */ lib.TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &img_width); lib.TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &img_height); surface = SDL_CreateRGBSurfaceWithFormat(0, img_width, img_height, 0, SDL_PIXELFORMAT_ABGR8888); if(!surface) goto error; if(!lib.TIFFReadRGBAImageOriented(tiff, img_width, img_height, (Uint32 *)surface->pixels, ORIENTATION_TOPLEFT, 0)) goto error; lib.TIFFClose(tiff); return surface; error: SDL_RWseek(src, start, RW_SEEK_SET); if (surface) { SDL_FreeSurface(surface); } if (tiff) { lib.TIFFClose(tiff); } return NULL; } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif int IMG_InitTIF() { IMG_SetError("TIFF images are not supported"); return(-1); } void IMG_QuitTIF() { } /* See if an image is contained in a data source */ int IMG_isTIF(SDL_RWops *src) { return(0); } /* Load a TIFF type image from an SDL datasource */ SDL_Surface *IMG_LoadTIF_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_TIF */ #endif /* !defined(__APPLE__) || defined(SDL_IMAGE_USE_COMMON_BACKEND) */ SDL2_image-2.8.8/src/IMG_webp.c0000664000000000000000000003401314751445211012720 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This is a WEBP image file loading framework */ #include "SDL_image.h" #ifdef LOAD_WEBP /*============================================================================= File: SDL_webp.c Purpose: A WEBP loader for the SDL library Revision: Created by: Michael Bonfils (Murlock) (26 November 2011) murlock42@gmail.com =============================================================================*/ #include "SDL_endian.h" #ifdef macintosh #define MACOS #endif #include #include static struct { int loaded; void *handle_libwebpdemux; void *handle_libwebp; VP8StatusCode (*WebPGetFeaturesInternal) (const uint8_t *data, size_t data_size, WebPBitstreamFeatures* features, int decoder_abi_version); uint8_t* (*WebPDecodeRGBInto) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride); uint8_t* (*WebPDecodeRGBAInto) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride); WebPDemuxer* (*WebPDemuxInternal)(const WebPData* data, int allow_partial, WebPDemuxState* state, int version); int (*WebPDemuxGetFrame)(const WebPDemuxer *dmux, int frame_number, WebPIterator *iter); int (*WebPDemuxNextFrame)(WebPIterator *iter); void (*WebPDemuxReleaseIterator)(WebPIterator *iter); uint32_t (*WebPDemuxGetI)(const WebPDemuxer* dmux, WebPFormatFeature feature); void (*WebPDemuxDelete)(WebPDemuxer* dmux); } lib; #if defined(LOAD_WEBP_DYNAMIC) && defined(LOAD_WEBPDEMUX_DYNAMIC) #define FUNCTION_LOADER_LIBWEBP(FUNC, SIG) \ lib.FUNC = (SIG) SDL_LoadFunction(lib.handle_libwebp, #FUNC); \ if (lib.FUNC == NULL) { SDL_UnloadObject(lib.handle_libwebp); return -1; } #define FUNCTION_LOADER_LIBWEBPDEMUX(FUNC, SIG) \ lib.FUNC = (SIG) SDL_LoadFunction(lib.handle_libwebpdemux, #FUNC); \ if (lib.FUNC == NULL) { SDL_UnloadObject(lib.handle_libwebpdemux); return -1; } #else #define FUNCTION_LOADER_LIBWEBP(FUNC, SIG) \ lib.FUNC = FUNC; \ if (lib.FUNC == NULL) { IMG_SetError("Missing webp.framework"); return -1; } #define FUNCTION_LOADER_LIBWEBPDEMUX(FUNC, SIG) \ lib.FUNC = FUNC; \ if (lib.FUNC == NULL) { IMG_SetError("Missing webpdemux.framework"); return -1; } #endif #ifdef __APPLE__ /* Need to turn off optimizations so weak framework load check works */ __attribute__ ((optnone)) #endif int IMG_InitWEBP() { if (lib.loaded == 0) { #if defined(LOAD_WEBP_DYNAMIC) && defined(LOAD_WEBPDEMUX_DYNAMIC) lib.handle_libwebpdemux = SDL_LoadObject(LOAD_WEBPDEMUX_DYNAMIC); if (lib.handle_libwebpdemux == NULL) { return -1; } lib.handle_libwebp = SDL_LoadObject(LOAD_WEBP_DYNAMIC); if (lib.handle_libwebp == NULL) { return -1; } #endif FUNCTION_LOADER_LIBWEBP(WebPGetFeaturesInternal, VP8StatusCode (*) (const uint8_t *data, size_t data_size, WebPBitstreamFeatures* features, int decoder_abi_version)) FUNCTION_LOADER_LIBWEBP(WebPDecodeRGBInto, uint8_t * (*) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride)) FUNCTION_LOADER_LIBWEBP(WebPDecodeRGBAInto, uint8_t * (*) (const uint8_t* data, size_t data_size, uint8_t* output_buffer, size_t output_buffer_size, int output_stride)) FUNCTION_LOADER_LIBWEBPDEMUX(WebPDemuxInternal, WebPDemuxer* (*)(const WebPData*, int, WebPDemuxState*, int)) FUNCTION_LOADER_LIBWEBPDEMUX(WebPDemuxGetFrame, int (*)(const WebPDemuxer *dmux, int frame_number, WebPIterator *iter)) FUNCTION_LOADER_LIBWEBPDEMUX(WebPDemuxNextFrame, int (*)(WebPIterator *iter)) FUNCTION_LOADER_LIBWEBPDEMUX(WebPDemuxReleaseIterator, void (*)(WebPIterator *iter)) FUNCTION_LOADER_LIBWEBPDEMUX(WebPDemuxGetI, uint32_t (*)(const WebPDemuxer* dmux, WebPFormatFeature feature)) FUNCTION_LOADER_LIBWEBPDEMUX(WebPDemuxDelete, void (*)(WebPDemuxer* dmux)) } ++lib.loaded; return 0; } void IMG_QuitWEBP() { if (lib.loaded == 0) { return; } if (lib.loaded == 1) { #if defined(LOAD_WEBP_DYNAMIC) && defined(LOAD_WEBPDEMUX_DYNAMIC) SDL_UnloadObject(lib.handle_libwebp); SDL_UnloadObject(lib.handle_libwebpdemux); #endif } --lib.loaded; } static int webp_getinfo (SDL_RWops *src, int *datasize) { Sint64 start; int is_WEBP; Uint8 magic[20]; if (!src) { return 0; } start = SDL_RWtell(src); is_WEBP = 0; if (SDL_RWread(src, magic, 1, sizeof(magic)) == sizeof(magic)) { if (magic[ 0] == 'R' && magic[ 1] == 'I' && magic[ 2] == 'F' && magic[ 3] == 'F' && magic[ 8] == 'W' && magic[ 9] == 'E' && magic[10] == 'B' && magic[11] == 'P' && magic[12] == 'V' && magic[13] == 'P' && magic[14] == '8' && (magic[15] == ' ' || magic[15] == 'X' || magic[15] == 'L')) { is_WEBP = 1; if (datasize) { *datasize = (int)(SDL_RWseek(src, 0, RW_SEEK_END) - start); } } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_WEBP); } /* See if an image is contained in a data source */ int IMG_isWEBP(SDL_RWops *src) { return webp_getinfo(src, NULL); } SDL_Surface *IMG_LoadWEBP_RW(SDL_RWops *src) { Sint64 start; const char *error = NULL; SDL_Surface *surface = NULL; Uint32 format; WebPBitstreamFeatures features; int raw_data_size; uint8_t *raw_data = NULL; int r; uint8_t *ret; if (!src) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); if ((IMG_Init(IMG_INIT_WEBP) & IMG_INIT_WEBP) == 0) { goto error; } raw_data_size = -1; if (!webp_getinfo(src, &raw_data_size)) { error = "Invalid WEBP"; goto error; } raw_data = (uint8_t*) SDL_malloc(raw_data_size); if (raw_data == NULL) { error = "Failed to allocate enough buffer for WEBP"; goto error; } r = (int)SDL_RWread(src, raw_data, 1, raw_data_size); if (r != raw_data_size) { error = "Failed to read WEBP"; goto error; } #if 0 {/* extract size of picture, not interesting since we don't know about alpha channel */ int width = -1, height = -1; if (!WebPGetInfo(raw_data, raw_data_size, &width, &height)) { printf("WebPGetInfo has failed\n"); return NULL; } } #endif if (lib.WebPGetFeaturesInternal(raw_data, raw_data_size, &features, WEBP_DECODER_ABI_VERSION) != VP8_STATUS_OK) { error = "WebPGetFeatures has failed"; goto error; } if (features.has_alpha) { format = SDL_PIXELFORMAT_RGBA32; } else { format = SDL_PIXELFORMAT_RGB24; } surface = SDL_CreateRGBSurfaceWithFormat(0, features.width, features.height, 0, format); if (surface == NULL) { error = "Failed to allocate SDL_Surface"; goto error; } if (features.has_alpha) { ret = lib.WebPDecodeRGBAInto(raw_data, raw_data_size, (uint8_t *)surface->pixels, surface->pitch * surface->h, surface->pitch); } else { ret = lib.WebPDecodeRGBInto(raw_data, raw_data_size, (uint8_t *)surface->pixels, surface->pitch * surface->h, surface->pitch); } if (!ret) { error = "Failed to decode WEBP"; goto error; } if (raw_data) { SDL_free(raw_data); } return surface; error: if (raw_data) { SDL_free(raw_data); } if (surface) { SDL_FreeSurface(surface); } if (error) { IMG_SetError("%s", error); } SDL_RWseek(src, start, RW_SEEK_SET); return NULL; } IMG_Animation *IMG_LoadWEBPAnimation_RW(SDL_RWops *src) { Sint64 start; const char *error = NULL; WebPBitstreamFeatures features; struct WebPDemuxer *demuxer = NULL; WebPIterator iter; IMG_Animation *anim = NULL; int raw_data_size; uint8_t *raw_data = NULL; WebPData wd; uint32_t bgcolor; SDL_Surface *canvas = NULL; WebPMuxAnimDispose dispose_method = WEBP_MUX_DISPOSE_BACKGROUND; if (!src) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); if ((IMG_Init(IMG_INIT_WEBP) & IMG_INIT_WEBP) == 0) { goto error; } raw_data_size = -1; if (!webp_getinfo(src, &raw_data_size)) { error = "Invalid WEBP Animation"; goto error; } raw_data = (uint8_t*) SDL_malloc(raw_data_size); if (raw_data == NULL) { goto error; } if ((int)SDL_RWread(src, raw_data, 1, raw_data_size) != raw_data_size) { goto error; } if (lib.WebPGetFeaturesInternal(raw_data, raw_data_size, &features, WEBP_DECODER_ABI_VERSION) != VP8_STATUS_OK) { error = "WebPGetFeatures() failed"; goto error; } wd.size = raw_data_size; wd.bytes = raw_data; demuxer = lib.WebPDemuxInternal(&wd, 0, NULL, WEBP_DEMUX_ABI_VERSION); if (!demuxer) { error = "WebPDemux() failed"; goto error; } anim = (IMG_Animation *)SDL_calloc(1, sizeof(*anim)); if (!anim) { goto error; } anim->w = features.width; anim->h = features.height; anim->count = lib.WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT); anim->frames = (SDL_Surface **)SDL_calloc(anim->count, sizeof(*anim->frames)); anim->delays = (int *)SDL_calloc(anim->count, sizeof(*anim->delays)); if (!anim->frames || !anim->delays) { goto error; } canvas = SDL_CreateRGBSurfaceWithFormat(0, anim->w, anim->h, 0, features.has_alpha ? SDL_PIXELFORMAT_RGBA32 : SDL_PIXELFORMAT_RGBX32); if (!canvas) { goto error; } /* Background color is BGRA byte order according to the spec */ bgcolor = lib.WebPDemuxGetI(demuxer, WEBP_FF_BACKGROUND_COLOR); #if SDL_BYTEORDER == SDL_BIG_ENDIAN bgcolor = SDL_MapRGBA(canvas->format, (bgcolor >> 8) & 0xFF, (bgcolor >> 16) & 0xFF, (bgcolor >> 24) & 0xFF, (bgcolor >> 0) & 0xFF); #else bgcolor = SDL_MapRGBA(canvas->format, (bgcolor >> 16) & 0xFF, (bgcolor >> 8) & 0xFF, (bgcolor >> 0) & 0xFF, (bgcolor >> 24) & 0xFF); #endif SDL_zero(iter); if (lib.WebPDemuxGetFrame(demuxer, 1, &iter)) { do { SDL_Surface* curr; SDL_Rect dst; int frame_idx = (iter.frame_num - 1); if (frame_idx < 0 || frame_idx >= anim->count) { continue; } if (dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) { SDL_FillRect(canvas, NULL, bgcolor); } curr = SDL_CreateRGBSurfaceWithFormat(0, iter.width, iter.height, 0, SDL_PIXELFORMAT_RGBA32); if (!curr) { goto error; } if (!lib.WebPDecodeRGBAInto(iter.fragment.bytes, iter.fragment.size, (uint8_t *)curr->pixels, curr->pitch * curr->h, curr->pitch)) { error = "WebPDecodeRGBAInto() failed"; SDL_FreeSurface(curr); goto error; } if (iter.blend_method == WEBP_MUX_BLEND) { SDL_SetSurfaceBlendMode(curr, SDL_BLENDMODE_BLEND); } else { SDL_SetSurfaceBlendMode(curr, SDL_BLENDMODE_NONE); } dst.x = iter.x_offset; dst.y = iter.y_offset; dst.w = iter.width; dst.h = iter.height; SDL_BlitSurface(curr, NULL, canvas, &dst); SDL_FreeSurface(curr); anim->frames[frame_idx] = SDL_DuplicateSurface(canvas); anim->delays[frame_idx] = iter.duration; dispose_method = iter.dispose_method; } while (lib.WebPDemuxNextFrame(&iter)); lib.WebPDemuxReleaseIterator(&iter); } SDL_FreeSurface(canvas); lib.WebPDemuxDelete(demuxer); SDL_free(raw_data); return anim; error: if (canvas) { SDL_FreeSurface(canvas); } if (anim) { IMG_FreeAnimation(anim); } if (demuxer) { lib.WebPDemuxDelete(demuxer); } if (raw_data) { SDL_free(raw_data); } if (error) { IMG_SetError("%s", error); } SDL_RWseek(src, start, RW_SEEK_SET); return NULL; } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif int IMG_InitWEBP() { IMG_SetError("WEBP images are not supported"); return -1; } void IMG_QuitWEBP() { } /* See if an image is contained in a data source */ int IMG_isWEBP(SDL_RWops *src) { return 0; } /* Load a WEBP type image from an SDL datasource */ SDL_Surface *IMG_LoadWEBP_RW(SDL_RWops *src) { return NULL; } IMG_Animation *IMG_LoadWEBPAnimation_RW(SDL_RWops *src) { return NULL; } #endif /* LOAD_WEBP */ SDL2_image-2.8.8/src/IMG_xcf.c0000664000000000000000000006041314751445211012546 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This is a XCF image file loading framework */ #include "SDL_endian.h" #include "SDL_image.h" #ifdef LOAD_XCF #ifndef SDL_SIZE_MAX #define SDL_SIZE_MAX ((size_t)-1) #endif #if DEBUG static char prop_names [][30] = { "end", "colormap", "active_layer", "active_channel", "selection", "floating_selection", "opacity", "mode", "visible", "linked", "preserve_transparency", "apply_mask", "edit_mask", "show_mask", "show_masked", "offsets", "color", "compression", "guides", "resolution", "tattoo", "parasites", "unit", "paths", "user_unit" }; #endif typedef enum { PROP_END = 0, PROP_COLORMAP = 1, PROP_ACTIVE_LAYER = 2, PROP_ACTIVE_CHANNEL = 3, PROP_SELECTION = 4, PROP_FLOATING_SELECTION = 5, PROP_OPACITY = 6, PROP_MODE = 7, PROP_VISIBLE = 8, PROP_LINKED = 9, PROP_PRESERVE_TRANSPARENCY = 10, PROP_APPLY_MASK = 11, PROP_EDIT_MASK = 12, PROP_SHOW_MASK = 13, PROP_SHOW_MASKED = 14, PROP_OFFSETS = 15, PROP_COLOR = 16, PROP_COMPRESSION = 17, PROP_GUIDES = 18, PROP_RESOLUTION = 19, PROP_TATTOO = 20, PROP_PARASITES = 21, PROP_UNIT = 22, PROP_PATHS = 23, PROP_USER_UNIT = 24 } xcf_prop_type; typedef enum { COMPR_NONE = 0, COMPR_RLE = 1, COMPR_ZLIB = 2, COMPR_FRACTAL = 3 } xcf_compr_type; typedef enum { IMAGE_RGB = 0, IMAGE_GREYSCALE = 1, IMAGE_INDEXED = 2 } xcf_image_type; typedef struct { Uint32 id; Uint32 length; union { struct { Uint32 num; char * cmap; } colormap; // 1 struct { Uint32 drawable_offset; } floating_selection; // 5 Sint32 opacity; Sint32 mode; int visible; int linked; int preserve_transparency; int apply_mask; int show_mask; struct { Sint32 x; Sint32 y; } offset; unsigned char color [3]; Uint8 compression; struct { Sint32 x; Sint32 y; } resolution; struct { char * name; Uint32 flags; Uint32 size; char * data; } parasite; } data; } xcf_prop; typedef struct { char sign [14]; Uint32 file_version; Uint32 width; Uint32 height; Sint32 image_type; Uint32 precision; xcf_prop * properties; Uint32 * layer_file_offsets; Uint32 * channel_file_offsets; xcf_compr_type compr; Uint32 cm_num; unsigned char * cm_map; } xcf_header; typedef struct { Uint32 width; Uint32 height; Sint32 layer_type; char * name; xcf_prop * properties; Uint64 hierarchy_file_offset; Uint64 layer_mask_offset; Uint32 offset_x; Uint32 offset_y; int visible; } xcf_layer; typedef struct { Uint32 width; Uint32 height; char * name; xcf_prop * properties; Uint64 hierarchy_file_offset; Uint32 color; Uint32 opacity; int selection; int visible; } xcf_channel; typedef struct { Uint32 width; Uint32 height; Uint32 bpp; Uint64 * level_file_offsets; } xcf_hierarchy; typedef struct { Uint32 width; Uint32 height; Uint64 * tile_file_offsets; } xcf_level; typedef unsigned char * xcf_tile; typedef unsigned char * (*load_tile_type) (SDL_RWops *, Uint64, int, int, int); /* See if an image is contained in a data source */ int IMG_isXCF(SDL_RWops *src) { Sint64 start; int is_XCF; char magic[14]; if ( !src ) return 0; start = SDL_RWtell(src); is_XCF = 0; if ( SDL_RWread(src, magic, sizeof(magic), 1) ) { if (SDL_strncmp(magic, "gimp xcf ", 9) == 0) { is_XCF = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_XCF); } static char * read_string (SDL_RWops * src) { Sint64 remaining; Uint32 tmp; char * data; tmp = SDL_ReadBE32(src); remaining = SDL_RWsize(src) - SDL_RWtell(src); if (tmp > 0 && (Sint32)tmp <= remaining) { data = (char *) SDL_malloc (sizeof (char) * tmp); if (data) { SDL_RWread(src, data, tmp, 1); data[tmp - 1] = '\0'; } } else { data = NULL; } return data; } static Uint64 read_offset (SDL_RWops * src, const xcf_header * h) { Uint64 offset; // starting with version 11, offsets are 64 bits offset = (h->file_version >= 11) ? (Uint64)SDL_ReadBE32 (src) << 32 : 0; offset |= SDL_ReadBE32 (src); return offset; } static Uint32 Swap32 (Uint32 v) { return ((v & 0x000000FF) << 16) | ((v & 0x0000FF00)) | ((v & 0x00FF0000) >> 16) | ((v & 0xFF000000)); } static int xcf_read_property (SDL_RWops * src, xcf_prop * prop) { Uint32 len; prop->id = SDL_ReadBE32 (src); prop->length = SDL_ReadBE32 (src); #if DEBUG printf ("%.8X: %s(%u): %u\n", SDL_RWtell (src), prop->id < 25 ? prop_names [prop->id] : "unknown", prop->id, prop->length); #endif switch (prop->id) { case PROP_COLORMAP: prop->data.colormap.num = SDL_ReadBE32 (src); prop->data.colormap.cmap = (char *) SDL_malloc (sizeof (char) * prop->data.colormap.num * 3); SDL_RWread (src, prop->data.colormap.cmap, prop->data.colormap.num*3, 1); break; case PROP_OFFSETS: prop->data.offset.x = SDL_ReadBE32 (src); prop->data.offset.y = SDL_ReadBE32 (src); break; case PROP_OPACITY: prop->data.opacity = SDL_ReadBE32 (src); break; case PROP_COMPRESSION: case PROP_COLOR: if (prop->length > sizeof(prop->data)) { len = sizeof(prop->data); } else { len = prop->length; } SDL_RWread(src, &prop->data, len, 1); break; case PROP_VISIBLE: prop->data.visible = SDL_ReadBE32 (src); break; default: // SDL_RWread (src, &prop->data, prop->length, 1); if (SDL_RWseek (src, prop->length, RW_SEEK_CUR) < 0) return 0; // ERROR } return 1; // OK } static void free_xcf_header (xcf_header * h) { if (h->cm_num) SDL_free (h->cm_map); if (h->layer_file_offsets) SDL_free (h->layer_file_offsets); SDL_free (h); } static xcf_header * read_xcf_header (SDL_RWops * src) { xcf_header * h; xcf_prop prop; h = (xcf_header *) SDL_malloc (sizeof (xcf_header)); if (!h) { return NULL; } SDL_RWread (src, h->sign, 14, 1); h->width = SDL_ReadBE32 (src); h->height = SDL_ReadBE32 (src); h->image_type = SDL_ReadBE32 (src); if (h->sign[9] == 'v' && h->sign[10] >= '0' && h->sign[10] <= '9' && h->sign[11] >= '0' && h->sign[11] <= '9' && h->sign[12] >= '0' && h->sign[12] <= '9') h->file_version = (h->sign[10] - '0') * 100 + (h->sign[11] - '0') * 10 + (h->sign[12] - '0'); else h->file_version = 0; #ifdef DEBUG printf ("XCF signature : %.14s (version %u)\n", h->sign, h->file_version); printf (" (%u,%u) type=%u\n", h->width, h->height, h->image_type); #endif if (h->file_version >= 4) h->precision = SDL_ReadBE32 (src); else h->precision = 150; h->properties = NULL; h->layer_file_offsets = NULL; h->compr = COMPR_NONE; h->cm_num = 0; h->cm_map = NULL; // Just read, don't save do { if (!xcf_read_property (src, &prop)) { free_xcf_header (h); return NULL; } if (prop.id == PROP_COMPRESSION) h->compr = (xcf_compr_type)prop.data.compression; else if (prop.id == PROP_COLORMAP) { // unused var: int i; Uint32 cm_num; unsigned char *cm_map; cm_num = prop.data.colormap.num; cm_map = (unsigned char *) SDL_realloc(h->cm_map, sizeof (unsigned char) * 3 * cm_num); if (cm_map) { h->cm_num = cm_num; h->cm_map = cm_map; SDL_memcpy (h->cm_map, prop.data.colormap.cmap, 3*sizeof (char)*h->cm_num); } SDL_free (prop.data.colormap.cmap); if (!cm_map) { free_xcf_header(h); return NULL; } } } while (prop.id != PROP_END); return h; } static void free_xcf_layer (xcf_layer * l) { SDL_free (l->name); SDL_free (l); } static xcf_layer * read_xcf_layer (SDL_RWops * src, const xcf_header * h) { xcf_layer * l; xcf_prop prop; l = (xcf_layer *) SDL_malloc (sizeof (xcf_layer)); l->width = SDL_ReadBE32 (src); l->height = SDL_ReadBE32 (src); l->layer_type = SDL_ReadBE32 (src); l->name = read_string (src); #ifdef DEBUG printf ("layer (%d,%d) type=%d '%s'\n", l->width, l->height, l->layer_type, l->name); #endif do { if (!xcf_read_property (src, &prop)) { free_xcf_layer (l); return NULL; } if (prop.id == PROP_OFFSETS) { l->offset_x = prop.data.offset.x; l->offset_y = prop.data.offset.y; } else if (prop.id == PROP_VISIBLE) { l->visible = prop.data.visible ? 1 : 0; } else if (prop.id == PROP_COLORMAP) { SDL_free (prop.data.colormap.cmap); } } while (prop.id != PROP_END); l->hierarchy_file_offset = read_offset (src, h); l->layer_mask_offset = read_offset (src, h); return l; } static void free_xcf_channel (xcf_channel * c) { SDL_free (c->name); SDL_free (c); } static xcf_channel * read_xcf_channel (SDL_RWops * src, const xcf_header * h) { xcf_channel * l; xcf_prop prop; l = (xcf_channel *) SDL_malloc (sizeof (xcf_channel)); l->width = SDL_ReadBE32 (src); l->height = SDL_ReadBE32 (src); l->name = read_string (src); #ifdef DEBUG printf ("channel (%u,%u) '%s'\n", l->width, l->height, l->name); #endif l->selection = 0; do { if (!xcf_read_property (src, &prop)) { free_xcf_channel (l); return NULL; } switch (prop.id) { case PROP_OPACITY: l->opacity = prop.data.opacity << 24; break; case PROP_COLOR: l->color = ((Uint32) prop.data.color[0] << 16) | ((Uint32) prop.data.color[1] << 8) | ((Uint32) prop.data.color[2]); break; case PROP_SELECTION: l->selection = 1; break; case PROP_VISIBLE: l->visible = prop.data.visible ? 1 : 0; break; default: ; } } while (prop.id != PROP_END); l->hierarchy_file_offset = read_offset (src, h); return l; } static void free_xcf_hierarchy (xcf_hierarchy * h) { SDL_free (h->level_file_offsets); SDL_free (h); } static xcf_hierarchy * read_xcf_hierarchy (SDL_RWops * src, const xcf_header * head) { xcf_hierarchy * h; int i; h = (xcf_hierarchy *) SDL_malloc (sizeof (xcf_hierarchy)); h->width = SDL_ReadBE32 (src); h->height = SDL_ReadBE32 (src); h->bpp = SDL_ReadBE32 (src); h->level_file_offsets = NULL; i = 0; do { h->level_file_offsets = (Uint64 *) SDL_realloc (h->level_file_offsets, sizeof (*h->level_file_offsets) * (i+1)); h->level_file_offsets [i] = read_offset (src, head); } while (h->level_file_offsets [i++]); return h; } static void free_xcf_level (xcf_level * l) { SDL_free (l->tile_file_offsets); SDL_free (l); } static xcf_level * read_xcf_level (SDL_RWops * src, const xcf_header * h) { xcf_level * l; int i; l = (xcf_level *) SDL_malloc (sizeof (xcf_level)); l->width = SDL_ReadBE32 (src); l->height = SDL_ReadBE32 (src); l->tile_file_offsets = NULL; i = 0; do { l->tile_file_offsets = (Uint64 *) SDL_realloc (l->tile_file_offsets, sizeof (*l->tile_file_offsets) * (i+1)); l->tile_file_offsets [i] = read_offset (src, h); } while (l->tile_file_offsets [i++]); return l; } static void free_xcf_tile (unsigned char * t) { SDL_free (t); } static unsigned char * load_xcf_tile_none (SDL_RWops * src, Uint64 len, int bpp, int x, int y) { unsigned char * load = NULL; if (len <= (Uint64)SDL_SIZE_MAX) { load = (unsigned char *) SDL_malloc ((size_t)len); // expect this is okay } if (load != NULL) SDL_RWread (src, load, (size_t)len, 1); return load; } static unsigned char * load_xcf_tile_rle (SDL_RWops * src, Uint64 len, int bpp, int x, int y) { unsigned char * load, * t, * data, * d; int i, size, count, j, length; unsigned char val; if (len == 0 || len > (Uint64)SDL_SIZE_MAX) { /* probably bogus data. */ return NULL; } t = load = (unsigned char *) SDL_malloc ((size_t)len); if (load == NULL) return NULL; SDL_RWread (src, t, 1, (size_t)len); /* reallen */ data = (unsigned char *) SDL_calloc (1, x*y*bpp); for (i = 0; i < bpp; i++) { d = data + i; size = x*y; count = 0; while (size > 0) { val = *t++; length = val; if (length >= 128) { length = 255 - (length - 1); if (length == 128) { length = (*t << 8) + t[1]; t += 2; } if (((size_t) (t - load) + length) >= len) { break; /* bogus data */ } else if (length > size) { break; /* bogus data */ } count += length; size -= length; while (length-- > 0) { *d = *t++; d += bpp; } } else { length += 1; if (length == 128) { length = (*t << 8) + t[1]; t += 2; } if (((size_t) (t - load)) >= len) { break; /* bogus data */ } else if (length > size) { break; /* bogus data */ } count += length; size -= length; val = *t++; for (j = 0; j < length; j++) { *d = val; d += bpp; } } } if (size > 0) { break; /* just drop out, untouched data initialized to zero. */ } } SDL_free (load); return (data); } static Uint32 rgb2grey (Uint32 a) { Uint8 l; l = (Uint8)(0.2990 * ((a & 0x00FF0000) >> 16) + 0.5870 * ((a & 0x0000FF00) >> 8) + 0.1140 * ((a & 0x000000FF))); return (l << 16) | (l << 8) | l; } static void create_channel_surface (SDL_Surface * surf, xcf_image_type itype, Uint32 color, Uint32 opacity) { Uint32 c = 0; switch (itype) { case IMAGE_RGB: case IMAGE_INDEXED: c = opacity | color; break; case IMAGE_GREYSCALE: c = opacity | rgb2grey (color); break; } SDL_FillRect (surf, NULL, c); } static int do_layer_surface(SDL_Surface * surface, SDL_RWops * src, xcf_header * head, xcf_layer * layer, load_tile_type load_tile) { xcf_hierarchy *hierarchy; xcf_level *level; unsigned char *tile; Uint8 *p8; Uint32 *p; int i, j; Uint32 x, y, tx, ty, ox, oy; Uint32 *row; Uint64 length; SDL_RWseek(src, layer->hierarchy_file_offset, RW_SEEK_SET); hierarchy = read_xcf_hierarchy(src, head); if (hierarchy->bpp > 4) { /* unsupported. */ SDL_Log("Unknown Gimp image bpp (%u)\n", (unsigned int) hierarchy->bpp); free_xcf_hierarchy(hierarchy); return 1; } if ((hierarchy->width > 20000) || (hierarchy->height > 20000)) { /* arbitrary limit to avoid integer overflow. */ SDL_Log("Gimp image too large (%ux%u)\n", (unsigned int) hierarchy->width, (unsigned int) hierarchy->height); free_xcf_hierarchy(hierarchy); return 1; } level = NULL; for (i = 0; hierarchy->level_file_offsets[i]; i++) { if (SDL_RWseek(src, hierarchy->level_file_offsets[i], RW_SEEK_SET) < 0) break; if (i > 0) // skip level except the 1st one, just like GIMP does continue; level = read_xcf_level(src, head); ty = tx = 0; for (j = 0; level->tile_file_offsets[j]; j++) { SDL_RWseek(src, level->tile_file_offsets[j], RW_SEEK_SET); ox = tx + 64 > level->width ? level->width % 64 : 64; oy = ty + 64 > level->height ? level->height % 64 : 64; length = ox*oy*6; if (level->tile_file_offsets[j + 1] > level->tile_file_offsets[j]) { length = level->tile_file_offsets[j + 1] - level->tile_file_offsets[j]; } tile = load_tile(src, length, hierarchy->bpp, ox, oy); if (!tile) { if (hierarchy) { free_xcf_hierarchy(hierarchy); } if (level) { free_xcf_level(level); } return 1; } p8 = tile; p = (Uint32 *) p8; for (y = ty; y < ty + oy; y++) { if ((y >= (Uint32)surface->h) || ((tx+ox) > (Uint32)surface->w)) { break; } row = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch + tx * 4); switch (hierarchy->bpp) { case 4: for (x = tx; x < tx + ox; x++) *row++ = Swap32(*p++); break; case 3: for (x = tx; x < tx + ox; x++) { *row = 0xFF000000; *row |= ((Uint32)*p8++ << 16); *row |= ((Uint32)*p8++ << 8); *row |= ((Uint32)*p8++ << 0); row++; } break; case 2: /* Indexed / Greyscale + Alpha */ switch (head->image_type) { case IMAGE_INDEXED: for (x = tx; x < tx + ox; x++) { *row = ((Uint32)(head->cm_map[*p8 * 3]) << 16); *row |= ((Uint32)(head->cm_map[*p8 * 3 + 1]) << 8); *row |= ((Uint32)(head->cm_map[*p8++ * 3 + 2]) << 0); *row |= ((Uint32)*p8++ << 24); row++; } break; case IMAGE_GREYSCALE: for (x = tx; x < tx + ox; x++) { *row = ((Uint32)*p8 << 16); *row |= ((Uint32)*p8 << 8); *row |= ((Uint32)*p8++ << 0); *row |= ((Uint32)*p8++ << 24); row++; } break; default: SDL_Log("Unknown Gimp image type (%d)\n", head->image_type); if (hierarchy) { free_xcf_hierarchy(hierarchy); } if (level) free_xcf_level(level); return 1; } break; case 1: /* Indexed / Greyscale */ switch (head->image_type) { case IMAGE_INDEXED: for (x = tx; x < tx + ox; x++) { *row++ = 0xFF000000 | ((Uint32)(head->cm_map[*p8 * 3]) << 16) | ((Uint32)(head->cm_map[*p8 * 3 + 1]) << 8) | ((Uint32)(head->cm_map[*p8 * 3 + 2]) << 0); p8++; } break; case IMAGE_GREYSCALE: for (x = tx; x < tx + ox; x++) { *row++ = 0xFF000000 | (((Uint32)(*p8)) << 16) | (((Uint32)(*p8)) << 8) | (((Uint32)(*p8)) << 0); ++p8; } break; default: SDL_Log("Unknown Gimp image type (%d)\n", head->image_type); if (tile) free_xcf_tile(tile); if (level) free_xcf_level(level); if (hierarchy) free_xcf_hierarchy(hierarchy); return 1; } break; } } free_xcf_tile(tile); tx += 64; if (tx >= level->width) { tx = 0; ty += 64; } if (ty >= level->height) { break; } } free_xcf_level(level); } free_xcf_hierarchy(hierarchy); return 0; } SDL_Surface *IMG_LoadXCF_RW(SDL_RWops *src) { Sint64 start; const char *error = NULL; SDL_Surface *surface, *lays; xcf_header * head; xcf_layer * layer; xcf_channel ** channel; int chnls, i, offsets; Sint64 offset, fp; unsigned char * (* load_tile) (SDL_RWops *, Uint64, int, int, int); if (!src) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); /* Initialize the data we will clean up when we're done */ surface = NULL; head = read_xcf_header(src); if (!head) { return NULL; } switch (head->compr) { case COMPR_NONE: load_tile = load_xcf_tile_none; break; case COMPR_RLE: load_tile = load_xcf_tile_rle; break; default: SDL_Log("Unsupported Compression.\n"); free_xcf_header (head); return NULL; } /* Create the surface of the appropriate type */ surface = SDL_CreateRGBSurfaceWithFormat(0, head->width, head->height, 0, SDL_PIXELFORMAT_ARGB8888); if ( surface == NULL ) { error = "Out of memory"; goto done; } offsets = 0; while ((offset = read_offset (src, head))) { head->layer_file_offsets = (Uint32 *) SDL_realloc (head->layer_file_offsets, sizeof (Uint32) * (offsets+1)); head->layer_file_offsets [offsets] = (Uint32)offset; offsets++; } fp = SDL_RWtell (src); lays = SDL_CreateRGBSurfaceWithFormat(0, head->width, head->height, 0, SDL_PIXELFORMAT_ARGB8888); if ( lays == NULL ) { error = "Out of memory"; goto done; } // Blit layers backwards, because Gimp saves them highest first for (i = offsets; i > 0; i--) { SDL_Rect rs, rd; SDL_RWseek (src, head->layer_file_offsets [i-1], RW_SEEK_SET); layer = read_xcf_layer (src, head); if (layer != NULL) { do_layer_surface (lays, src, head, layer, load_tile); rs.x = 0; rs.y = 0; rs.w = layer->width; rs.h = layer->height; rd.x = layer->offset_x; rd.y = layer->offset_y; rd.w = layer->width; rd.h = layer->height; if (layer->visible) SDL_BlitSurface (lays, &rs, surface, &rd); free_xcf_layer (layer); } } SDL_FreeSurface (lays); SDL_RWseek (src, fp, RW_SEEK_SET); // read channels channel = NULL; chnls = 0; while ((offset = read_offset (src, head))) { channel = (xcf_channel **) SDL_realloc (channel, sizeof (xcf_channel *) * (chnls+1)); fp = SDL_RWtell (src); SDL_RWseek (src, offset, RW_SEEK_SET); channel [chnls] = (read_xcf_channel (src, head)); if (channel [chnls] != NULL) chnls++; SDL_RWseek (src, fp, RW_SEEK_SET); } if (chnls) { SDL_Surface * chs; chs = SDL_CreateRGBSurfaceWithFormat(0, head->width, head->height, 0, SDL_PIXELFORMAT_ARGB8888); if (chs == NULL) { error = "Out of memory"; goto done; } for (i = 0; i < chnls; i++) { // printf ("CNLBLT %i\n", i); if (!channel [i]->selection && channel [i]->visible) { create_channel_surface (chs, (xcf_image_type)head->image_type, channel [i]->color, channel [i]->opacity); SDL_BlitSurface (chs, NULL, surface, NULL); } free_xcf_channel (channel [i]); } SDL_free(channel); SDL_FreeSurface (chs); } done: free_xcf_header (head); if ( error ) { SDL_RWseek(src, start, RW_SEEK_SET); if ( surface ) { SDL_FreeSurface(surface); surface = NULL; } IMG_SetError("%s", error); } return(surface); } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif /* See if an image is contained in a data source */ int IMG_isXCF(SDL_RWops *src) { return(0); } /* Load a XCF type image from an SDL datasource */ SDL_Surface *IMG_LoadXCF_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_XCF */ SDL2_image-2.8.8/src/IMG_xpm.c0000664000000000000000000013513114751445211012572 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* * XPM (X PixMap) image loader: * * Supports the XPMv3 format, EXCEPT: * - hotspot coordinates are ignored * - only colour ('c') colour symbols are used * - rgb.txt is not used (for portability), so only RGB colours * are recognized (#rrggbb etc) - only a few basic colour names are * handled * * The result is an 8bpp indexed surface if possible, otherwise 32bpp. * The colourkey is correctly set if transparency is used. * * Besides the standard API, also provides * * SDL_Surface *IMG_ReadXPMFromArray(char **xpm) * SDL_Surface *IMG_ReadXPMFromArrayToRGB888(char **xpm) * * that read the image data from an XPM file included in the C source. * - 1st function returns an 8bpp indexed surface if possible, otherwise 32bpp. * - 2nd function returns always a 32bpp (RGB888) surface * * TODO: include rgb.txt here. The full table (from solaris 2.6) only * requires about 13K in binary form. */ #include "SDL_image.h" #ifdef LOAD_XPM /* See if an image is contained in a data source */ int IMG_isXPM(SDL_RWops *src) { Sint64 start; int is_XPM; char magic[9]; if ( !src ) return 0; start = SDL_RWtell(src); is_XPM = 0; if ( SDL_RWread(src, magic, sizeof(magic), 1) ) { if ( SDL_memcmp(magic, "/* XPM */", sizeof(magic)) == 0 ) { is_XPM = 1; } } SDL_RWseek(src, start, RW_SEEK_SET); return(is_XPM); } /* Hash table to look up colors from pixel strings */ #define STARTING_HASH_SIZE 256 struct hash_entry { char *key; Uint32 color; struct hash_entry *next; }; struct color_hash { struct hash_entry **table; struct hash_entry *entries; /* array of all entries */ struct hash_entry *next_free; int size; int maxnum; }; static int hash_key(const char *key, int cpp, int size) { int hash; hash = 0; while ( cpp-- > 0 ) { hash = hash * 33 + *key++; } return hash & (size - 1); } static struct color_hash *create_colorhash(int maxnum) { int bytes, s; struct color_hash *hash; /* we know how many entries we need, so we can allocate everything here */ hash = (struct color_hash *)SDL_calloc(1, sizeof(*hash)); if (!hash) return NULL; /* use power-of-2 sized hash table for decoding speed */ for (s = STARTING_HASH_SIZE; s < maxnum; s <<= 1) ; hash->size = s; hash->maxnum = maxnum; bytes = hash->size * sizeof(struct hash_entry **); /* Check for overflow */ if ((bytes / sizeof(struct hash_entry **)) != hash->size) { IMG_SetError("memory allocation overflow"); SDL_free(hash); return NULL; } hash->table = (struct hash_entry **)SDL_calloc(1, bytes); if (!hash->table) { SDL_free(hash); return NULL; } bytes = maxnum * sizeof(struct hash_entry); /* Check for overflow */ if ((bytes / sizeof(struct hash_entry)) != maxnum) { IMG_SetError("memory allocation overflow"); SDL_free(hash->table); SDL_free(hash); return NULL; } hash->entries = (struct hash_entry *)SDL_calloc(1, bytes); if (!hash->entries) { SDL_free(hash->table); SDL_free(hash); return NULL; } hash->next_free = hash->entries; return hash; } static int add_colorhash(struct color_hash *hash, char *key, int cpp, Uint32 color) { int index = hash_key(key, cpp, hash->size); struct hash_entry *e = hash->next_free++; e->color = color; e->key = key; e->next = hash->table[index]; hash->table[index] = e; return 1; } /* fast lookup that works if cpp == 1 */ #define QUICK_COLORHASH(hash, key) ((hash)->table[*(Uint8 *)(key)]->color) static Uint32 get_colorhash(struct color_hash *hash, const char *key, int cpp) { struct hash_entry *entry = hash->table[hash_key(key, cpp, hash->size)]; while (entry) { if (SDL_memcmp(key, entry->key, cpp) == 0) return entry->color; entry = entry->next; } return 0; /* garbage in - garbage out */ } static void free_colorhash(struct color_hash *hash) { if (hash) { if (hash->table) SDL_free(hash->table); if (hash->entries) SDL_free(hash->entries); SDL_free(hash); } } /* * convert colour spec to RGB (in 0xaarrggbb format). * return 1 if successful. */ static int color_to_argb(char *spec, int speclen, Uint32 *argb) { /* poor man's rgb.txt */ static struct { char *name; Uint32 argb; } known[] = { { "none", 0x00000000 }, { "black", 0xff000000 }, { "white", 0xffFFFFFF }, { "red", 0xffFF0000 }, { "green", 0xff00FF00 }, { "blue", 0xff0000FF }, /* This table increases the size of the library by 40K, so it's disabled by default */ #ifdef EXTENDED_XPM_COLORS { "aliceblue", 0xfff0f8ff }, { "antiquewhite", 0xfffaebd7 }, { "antiquewhite1", 0xffffefdb }, { "antiquewhite2", 0xffeedfcc }, { "antiquewhite3", 0xffcdc0b0 }, { "antiquewhite4", 0xff8b8378 }, { "aqua", 0xff00ffff }, { "aquamarine", 0xff7fffd4 }, { "aquamarine1", 0xff7fffd4 }, { "aquamarine2", 0xff76eec6 }, { "aquamarine3", 0xff66cdaa }, { "aquamarine4", 0xff458b74 }, { "azure", 0xfff0ffff }, { "azure1", 0xfff0ffff }, { "azure2", 0xffe0eeee }, { "azure3", 0xffc1cdcd }, { "azure4", 0xff838b8b }, { "beige", 0xfff5f5dc }, { "bisque", 0xffffe4c4 }, { "bisque1", 0xffffe4c4 }, { "bisque2", 0xffeed5b7 }, { "bisque3", 0xffcdb79e }, { "bisque4", 0xff8b7d6b }, { "black", 0xff000000 }, { "blanchedalmond", 0xffffebcd }, { "blue", 0xff0000ff }, { "blue1", 0xff0000ff }, { "blue2", 0xff0000ee }, { "blue3", 0xff0000cd }, { "blue4", 0xff00008B }, { "blueviolet", 0xff8a2be2 }, { "brown", 0xffA52A2A }, { "brown1", 0xffFF4040 }, { "brown2", 0xffEE3B3B }, { "brown3", 0xffCD3333 }, { "brown4", 0xff8B2323 }, { "burlywood", 0xffDEB887 }, { "burlywood1", 0xffFFD39B }, { "burlywood2", 0xffEEC591 }, { "burlywood3", 0xffCDAA7D }, { "burlywood4", 0xff8B7355 }, { "cadetblue", 0xff5F9EA0 }, { "cadetblue", 0xff5f9ea0 }, { "cadetblue1", 0xff98f5ff }, { "cadetblue2", 0xff8ee5ee }, { "cadetblue3", 0xff7ac5cd }, { "cadetblue4", 0xff53868b }, { "chartreuse", 0xff7FFF00 }, { "chartreuse1", 0xff7FFF00 }, { "chartreuse2", 0xff76EE00 }, { "chartreuse3", 0xff66CD00 }, { "chartreuse4", 0xff458B00 }, { "chocolate", 0xffD2691E }, { "chocolate1", 0xffFF7F24 }, { "chocolate2", 0xffEE7621 }, { "chocolate3", 0xffCD661D }, { "chocolate4", 0xff8B4513 }, { "coral", 0xffFF7F50 }, { "coral1", 0xffFF7256 }, { "coral2", 0xffEE6A50 }, { "coral3", 0xffCD5B45 }, { "coral4", 0xff8B3E2F }, { "cornflowerblue", 0xff6495ed }, { "cornsilk", 0xffFFF8DC }, { "cornsilk1", 0xffFFF8DC }, { "cornsilk2", 0xffEEE8CD }, { "cornsilk3", 0xffCDC8B1 }, { "cornsilk4", 0xff8B8878 }, { "crimson", 0xffDC143C }, { "cyan", 0xff00FFFF }, { "cyan1", 0xff00FFFF }, { "cyan2", 0xff00EEEE }, { "cyan3", 0xff00CDCD }, { "cyan4", 0xff008B8B }, { "darkblue", 0xff00008b }, { "darkcyan", 0xff008b8b }, { "darkgoldenrod", 0xffb8860b }, { "darkgoldenrod1", 0xffffb90f }, { "darkgoldenrod2", 0xffeead0e }, { "darkgoldenrod3", 0xffcd950c }, { "darkgoldenrod4", 0xff8b6508 }, { "darkgray", 0xffa9a9a9 }, { "darkgreen", 0xff006400 }, { "darkgrey", 0xffa9a9a9 }, { "darkkhaki", 0xffbdb76b }, { "darkmagenta", 0xff8b008b }, { "darkolivegreen", 0xff556b2f }, { "darkolivegreen1", 0xffcaff70 }, { "darkolivegreen2", 0xffbcee68 }, { "darkolivegreen3", 0xffa2cd5a }, { "darkolivegreen4", 0xff6e8b3d }, { "darkorange", 0xffff8c00 }, { "darkorange1", 0xffff7f00 }, { "darkorange2", 0xffee7600 }, { "darkorange3", 0xffcd6600 }, { "darkorange4", 0xff8b4500 }, { "darkorchid", 0xff9932cc }, { "darkorchid1", 0xffbf3eff }, { "darkorchid2", 0xffb23aee }, { "darkorchid3", 0xff9a32cd }, { "darkorchid4", 0xff68228b }, { "darkred", 0xff8b0000 }, { "darksalmon", 0xffe9967a }, { "darkseagreen", 0xff8fbc8f }, { "darkseagreen1", 0xffc1ffc1 }, { "darkseagreen2", 0xffb4eeb4 }, { "darkseagreen3", 0xff9bcd9b }, { "darkseagreen4", 0xff698b69 }, { "darkslateblue", 0xff483d8b }, { "darkslategray", 0xff2f4f4f }, { "darkslategray1", 0xff97ffff }, { "darkslategray2", 0xff8deeee }, { "darkslategray3", 0xff79cdcd }, { "darkslategray4", 0xff528b8b }, { "darkslategrey", 0xff2f4f4f }, { "darkturquoise", 0xff00ced1 }, { "darkviolet", 0xff9400D3 }, { "darkviolet", 0xff9400d3 }, { "deeppink", 0xffff1493 }, { "deeppink1", 0xffff1493 }, { "deeppink2", 0xffee1289 }, { "deeppink3", 0xffcd1076 }, { "deeppink4", 0xff8b0a50 }, { "deepskyblue", 0xff00bfff }, { "deepskyblue1", 0xff00bfff }, { "deepskyblue2", 0xff00b2ee }, { "deepskyblue3", 0xff009acd }, { "deepskyblue4", 0xff00688b }, { "dimgray", 0xff696969 }, { "dimgrey", 0xff696969 }, { "dodgerblue", 0xff1e90ff }, { "dodgerblue1", 0xff1e90ff }, { "dodgerblue2", 0xff1c86ee }, { "dodgerblue3", 0xff1874cd }, { "dodgerblue4", 0xff104e8b }, { "firebrick", 0xffB22222 }, { "firebrick1", 0xffFF3030 }, { "firebrick2", 0xffEE2C2C }, { "firebrick3", 0xffCD2626 }, { "firebrick4", 0xff8B1A1A }, { "floralwhite", 0xfffffaf0 }, { "forestgreen", 0xff228b22 }, { "fractal", 0xff808080 }, { "fuchsia", 0xffFF00FF }, { "gainsboro", 0xffDCDCDC }, { "ghostwhite", 0xfff8f8ff }, { "gold", 0xffFFD700 }, { "gold1", 0xffFFD700 }, { "gold2", 0xffEEC900 }, { "gold3", 0xffCDAD00 }, { "gold4", 0xff8B7500 }, { "goldenrod", 0xffDAA520 }, { "goldenrod1", 0xffFFC125 }, { "goldenrod2", 0xffEEB422 }, { "goldenrod3", 0xffCD9B1D }, { "goldenrod4", 0xff8B6914 }, { "gray", 0xff7E7E7E }, { "gray", 0xffBEBEBE }, { "gray0", 0xff000000 }, { "gray1", 0xff030303 }, { "gray10", 0xff1A1A1A }, { "gray100", 0xffFFFFFF }, { "gray11", 0xff1C1C1C }, { "gray12", 0xff1F1F1F }, { "gray13", 0xff212121 }, { "gray14", 0xff242424 }, { "gray15", 0xff262626 }, { "gray16", 0xff292929 }, { "gray17", 0xff2B2B2B }, { "gray18", 0xff2E2E2E }, { "gray19", 0xff303030 }, { "gray2", 0xff050505 }, { "gray20", 0xff333333 }, { "gray21", 0xff363636 }, { "gray22", 0xff383838 }, { "gray23", 0xff3B3B3B }, { "gray24", 0xff3D3D3D }, { "gray25", 0xff404040 }, { "gray26", 0xff424242 }, { "gray27", 0xff454545 }, { "gray28", 0xff474747 }, { "gray29", 0xff4A4A4A }, { "gray3", 0xff080808 }, { "gray30", 0xff4D4D4D }, { "gray31", 0xff4F4F4F }, { "gray32", 0xff525252 }, { "gray33", 0xff545454 }, { "gray34", 0xff575757 }, { "gray35", 0xff595959 }, { "gray36", 0xff5C5C5C }, { "gray37", 0xff5E5E5E }, { "gray38", 0xff616161 }, { "gray39", 0xff636363 }, { "gray4", 0xff0A0A0A }, { "gray40", 0xff666666 }, { "gray41", 0xff696969 }, { "gray42", 0xff6B6B6B }, { "gray43", 0xff6E6E6E }, { "gray44", 0xff707070 }, { "gray45", 0xff737373 }, { "gray46", 0xff757575 }, { "gray47", 0xff787878 }, { "gray48", 0xff7A7A7A }, { "gray49", 0xff7D7D7D }, { "gray5", 0xff0D0D0D }, { "gray50", 0xff7F7F7F }, { "gray51", 0xff828282 }, { "gray52", 0xff858585 }, { "gray53", 0xff878787 }, { "gray54", 0xff8A8A8A }, { "gray55", 0xff8C8C8C }, { "gray56", 0xff8F8F8F }, { "gray57", 0xff919191 }, { "gray58", 0xff949494 }, { "gray59", 0xff969696 }, { "gray6", 0xff0F0F0F }, { "gray60", 0xff999999 }, { "gray61", 0xff9C9C9C }, { "gray62", 0xff9E9E9E }, { "gray63", 0xffA1A1A1 }, { "gray64", 0xffA3A3A3 }, { "gray65", 0xffA6A6A6 }, { "gray66", 0xffA8A8A8 }, { "gray67", 0xffABABAB }, { "gray68", 0xffADADAD }, { "gray69", 0xffB0B0B0 }, { "gray7", 0xff121212 }, { "gray70", 0xffB3B3B3 }, { "gray71", 0xffB5B5B5 }, { "gray72", 0xffB8B8B8 }, { "gray73", 0xffBABABA }, { "gray74", 0xffBDBDBD }, { "gray75", 0xffBFBFBF }, { "gray76", 0xffC2C2C2 }, { "gray77", 0xffC4C4C4 }, { "gray78", 0xffC7C7C7 }, { "gray79", 0xffC9C9C9 }, { "gray8", 0xff141414 }, { "gray80", 0xffCCCCCC }, { "gray81", 0xffCFCFCF }, { "gray82", 0xffD1D1D1 }, { "gray83", 0xffD4D4D4 }, { "gray84", 0xffD6D6D6 }, { "gray85", 0xffD9D9D9 }, { "gray86", 0xffDBDBDB }, { "gray87", 0xffDEDEDE }, { "gray88", 0xffE0E0E0 }, { "gray89", 0xffE3E3E3 }, { "gray9", 0xff171717 }, { "gray90", 0xffE5E5E5 }, { "gray91", 0xffE8E8E8 }, { "gray92", 0xffEBEBEB }, { "gray93", 0xffEDEDED }, { "gray94", 0xffF0F0F0 }, { "gray95", 0xffF2F2F2 }, { "gray96", 0xffF5F5F5 }, { "gray97", 0xffF7F7F7 }, { "gray98", 0xffFAFAFA }, { "gray99", 0xffFCFCFC }, { "green", 0xff008000 }, { "green", 0xff00FF00 }, { "green1", 0xff00FF00 }, { "green2", 0xff00EE00 }, { "green3", 0xff00CD00 }, { "green4", 0xff008B00 }, { "greenyellow", 0xffadff2f }, { "grey", 0xffBEBEBE }, { "grey0", 0xff000000 }, { "grey1", 0xff030303 }, { "grey10", 0xff1A1A1A }, { "grey100", 0xffFFFFFF }, { "grey11", 0xff1C1C1C }, { "grey12", 0xff1F1F1F }, { "grey13", 0xff212121 }, { "grey14", 0xff242424 }, { "grey15", 0xff262626 }, { "grey16", 0xff292929 }, { "grey17", 0xff2B2B2B }, { "grey18", 0xff2E2E2E }, { "grey19", 0xff303030 }, { "grey2", 0xff050505 }, { "grey20", 0xff333333 }, { "grey21", 0xff363636 }, { "grey22", 0xff383838 }, { "grey23", 0xff3B3B3B }, { "grey24", 0xff3D3D3D }, { "grey25", 0xff404040 }, { "grey26", 0xff424242 }, { "grey27", 0xff454545 }, { "grey28", 0xff474747 }, { "grey29", 0xff4A4A4A }, { "grey3", 0xff080808 }, { "grey30", 0xff4D4D4D }, { "grey31", 0xff4F4F4F }, { "grey32", 0xff525252 }, { "grey33", 0xff545454 }, { "grey34", 0xff575757 }, { "grey35", 0xff595959 }, { "grey36", 0xff5C5C5C }, { "grey37", 0xff5E5E5E }, { "grey38", 0xff616161 }, { "grey39", 0xff636363 }, { "grey4", 0xff0A0A0A }, { "grey40", 0xff666666 }, { "grey41", 0xff696969 }, { "grey42", 0xff6B6B6B }, { "grey43", 0xff6E6E6E }, { "grey44", 0xff707070 }, { "grey45", 0xff737373 }, { "grey46", 0xff757575 }, { "grey47", 0xff787878 }, { "grey48", 0xff7A7A7A }, { "grey49", 0xff7D7D7D }, { "grey5", 0xff0D0D0D }, { "grey50", 0xff7F7F7F }, { "grey51", 0xff828282 }, { "grey52", 0xff858585 }, { "grey53", 0xff878787 }, { "grey54", 0xff8A8A8A }, { "grey55", 0xff8C8C8C }, { "grey56", 0xff8F8F8F }, { "grey57", 0xff919191 }, { "grey58", 0xff949494 }, { "grey59", 0xff969696 }, { "grey6", 0xff0F0F0F }, { "grey60", 0xff999999 }, { "grey61", 0xff9C9C9C }, { "grey62", 0xff9E9E9E }, { "grey63", 0xffA1A1A1 }, { "grey64", 0xffA3A3A3 }, { "grey65", 0xffA6A6A6 }, { "grey66", 0xffA8A8A8 }, { "grey67", 0xffABABAB }, { "grey68", 0xffADADAD }, { "grey69", 0xffB0B0B0 }, { "grey7", 0xff121212 }, { "grey70", 0xffB3B3B3 }, { "grey71", 0xffB5B5B5 }, { "grey72", 0xffB8B8B8 }, { "grey73", 0xffBABABA }, { "grey74", 0xffBDBDBD }, { "grey75", 0xffBFBFBF }, { "grey76", 0xffC2C2C2 }, { "grey77", 0xffC4C4C4 }, { "grey78", 0xffC7C7C7 }, { "grey79", 0xffC9C9C9 }, { "grey8", 0xff141414 }, { "grey80", 0xffCCCCCC }, { "grey81", 0xffCFCFCF }, { "grey82", 0xffD1D1D1 }, { "grey83", 0xffD4D4D4 }, { "grey84", 0xffD6D6D6 }, { "grey85", 0xffD9D9D9 }, { "grey86", 0xffDBDBDB }, { "grey87", 0xffDEDEDE }, { "grey88", 0xffE0E0E0 }, { "grey89", 0xffE3E3E3 }, { "grey9", 0xff171717 }, { "grey90", 0xffE5E5E5 }, { "grey91", 0xffE8E8E8 }, { "grey92", 0xffEBEBEB }, { "grey93", 0xffEDEDED }, { "grey94", 0xffF0F0F0 }, { "grey95", 0xffF2F2F2 }, { "grey96", 0xffF5F5F5 }, { "grey97", 0xffF7F7F7 }, { "grey98", 0xffFAFAFA }, { "grey99", 0xffFCFCFC }, { "honeydew", 0xffF0FFF0 }, { "honeydew1", 0xffF0FFF0 }, { "honeydew2", 0xffE0EEE0 }, { "honeydew3", 0xffC1CDC1 }, { "honeydew4", 0xff838B83 }, { "hotpink", 0xffff69b4 }, { "hotpink1", 0xffff6eb4 }, { "hotpink2", 0xffee6aa7 }, { "hotpink3", 0xffcd6090 }, { "hotpink4", 0xff8b3a62 }, { "indianred", 0xffcd5c5c }, { "indianred1", 0xffff6a6a }, { "indianred2", 0xffee6363 }, { "indianred3", 0xffcd5555 }, { "indianred4", 0xff8b3a3a }, { "indigo", 0xff4B0082 }, { "ivory", 0xffFFFFF0 }, { "ivory1", 0xffFFFFF0 }, { "ivory2", 0xffEEEEE0 }, { "ivory3", 0xffCDCDC1 }, { "ivory4", 0xff8B8B83 }, { "khaki", 0xffF0E68C }, { "khaki1", 0xffFFF68F }, { "khaki2", 0xffEEE685 }, { "khaki3", 0xffCDC673 }, { "khaki4", 0xff8B864E }, { "lavender", 0xffE6E6FA }, { "lavenderblush", 0xfffff0f5 }, { "lavenderblush1", 0xfffff0f5 }, { "lavenderblush2", 0xffeee0e5 }, { "lavenderblush3", 0xffcdc1c5 }, { "lavenderblush4", 0xff8b8386 }, { "lawngreen", 0xff7cfc00 }, { "lemonchiffon", 0xfffffacd }, { "lemonchiffon1", 0xfffffacd }, { "lemonchiffon2", 0xffeee9bf }, { "lemonchiffon3", 0xffcdc9a5 }, { "lemonchiffon4", 0xff8b8970 }, { "lightblue", 0xffadd8e6 }, { "lightblue1", 0xffbfefff }, { "lightblue2", 0xffb2dfee }, { "lightblue3", 0xff9ac0cd }, { "lightblue4", 0xff68838b }, { "lightcoral", 0xfff08080 }, { "lightcyan", 0xffe0ffff }, { "lightcyan1", 0xffe0ffff }, { "lightcyan2", 0xffd1eeee }, { "lightcyan3", 0xffb4cdcd }, { "lightcyan4", 0xff7a8b8b }, { "lightgoldenrod", 0xffeedd82 }, { "lightgoldenrod1", 0xffffec8b }, { "lightgoldenrod2", 0xffeedc82 }, { "lightgoldenrod3", 0xffcdbe70 }, { "lightgoldenrod4", 0xff8b814c }, { "lightgoldenrodyellow", 0xfffafad2 }, { "lightgray", 0xffd3d3d3 }, { "lightgreen", 0xff90ee90 }, { "lightgrey", 0xffd3d3d3 }, { "lightpink", 0xffffb6c1 }, { "lightpink1", 0xffffaeb9 }, { "lightpink2", 0xffeea2ad }, { "lightpink3", 0xffcd8c95 }, { "lightpink4", 0xff8b5f65 }, { "lightsalmon", 0xffffa07a }, { "lightsalmon1", 0xffffa07a }, { "lightsalmon2", 0xffee9572 }, { "lightsalmon3", 0xffcd8162 }, { "lightsalmon4", 0xff8b5742 }, { "lightseagreen", 0xff20b2aa }, { "lightskyblue", 0xff87cefa }, { "lightskyblue1", 0xffb0e2ff }, { "lightskyblue2", 0xffa4d3ee }, { "lightskyblue3", 0xff8db6cd }, { "lightskyblue4", 0xff607b8b }, { "lightslateblue", 0xff8470ff }, { "lightslategray", 0xff778899 }, { "lightslategrey", 0xff778899 }, { "lightsteelblue", 0xffb0c4de }, { "lightsteelblue1", 0xffcae1ff }, { "lightsteelblue2", 0xffbcd2ee }, { "lightsteelblue3", 0xffa2b5cd }, { "lightsteelblue4", 0xff6e7b8b }, { "lightyellow", 0xffffffe0 }, { "lightyellow1", 0xffffffe0 }, { "lightyellow2", 0xffeeeed1 }, { "lightyellow3", 0xffcdcdb4 }, { "lightyellow4", 0xff8b8b7a }, { "lime", 0xff00FF00 }, { "limegreen", 0xff32cd32 }, { "linen", 0xffFAF0E6 }, { "magenta", 0xffFF00FF }, { "magenta1", 0xffFF00FF }, { "magenta2", 0xffEE00EE }, { "magenta3", 0xffCD00CD }, { "magenta4", 0xff8B008B }, { "maroon", 0xff800000 }, { "maroon", 0xffB03060 }, { "maroon1", 0xffFF34B3 }, { "maroon2", 0xffEE30A7 }, { "maroon3", 0xffCD2990 }, { "maroon4", 0xff8B1C62 }, { "mediumaquamarine", 0xff66cdaa }, { "mediumblue", 0xff0000cd }, { "mediumforestgreen", 0xff32814b }, { "mediumgoldenrod", 0xffd1c166 }, { "mediumorchid", 0xffba55d3 }, { "mediumorchid1", 0xffe066ff }, { "mediumorchid2", 0xffd15fee }, { "mediumorchid3", 0xffb452cd }, { "mediumorchid4", 0xff7a378b }, { "mediumpurple", 0xff9370db }, { "mediumpurple1", 0xffab82ff }, { "mediumpurple2", 0xff9f79ee }, { "mediumpurple3", 0xff8968cd }, { "mediumpurple4", 0xff5d478b }, { "mediumseagreen", 0xff3cb371 }, { "mediumslateblue", 0xff7b68ee }, { "mediumspringgreen", 0xff00fa9a }, { "mediumturquoise", 0xff48d1cc }, { "mediumvioletred", 0xffc71585 }, { "midnightblue", 0xff191970 }, { "mintcream", 0xfff5fffa }, { "mistyrose", 0xffffe4e1 }, { "mistyrose1", 0xffffe4e1 }, { "mistyrose2", 0xffeed5d2 }, { "mistyrose3", 0xffcdb7b5 }, { "mistyrose4", 0xff8b7d7b }, { "moccasin", 0xffFFE4B5 }, { "navajowhite", 0xffffdead }, { "navajowhite1", 0xffffdead }, { "navajowhite2", 0xffeecfa1 }, { "navajowhite3", 0xffcdb38b }, { "navajowhite4", 0xff8b795e }, { "navy", 0xff000080 }, { "navyblue", 0xff000080 }, { "none", 0xff0000FF }, { "oldlace", 0xfffdf5e6 }, { "olive", 0xff808000 }, { "olivedrab", 0xff6b8e23 }, { "olivedrab1", 0xffc0ff3e }, { "olivedrab2", 0xffb3ee3a }, { "olivedrab3", 0xff9acd32 }, { "olivedrab4", 0xff698b22 }, { "opaque", 0xff000000 }, { "orange", 0xffFFA500 }, { "orange1", 0xffFFA500 }, { "orange2", 0xffEE9A00 }, { "orange3", 0xffCD8500 }, { "orange4", 0xff8B5A00 }, { "orangered", 0xffff4500 }, { "orangered1", 0xffff4500 }, { "orangered2", 0xffee4000 }, { "orangered3", 0xffcd3700 }, { "orangered4", 0xff8b2500 }, { "orchid", 0xffDA70D6 }, { "orchid1", 0xffFF83FA }, { "orchid2", 0xffEE7AE9 }, { "orchid3", 0xffCD69C9 }, { "orchid4", 0xff8B4789 }, { "palegoldenrod", 0xffeee8aa }, { "palegreen", 0xff98fb98 }, { "palegreen1", 0xff9aff9a }, { "palegreen2", 0xff90ee90 }, { "palegreen3", 0xff7ccd7c }, { "palegreen4", 0xff548b54 }, { "paleturquoise", 0xffafeeee }, { "paleturquoise1", 0xffbbffff }, { "paleturquoise2", 0xffaeeeee }, { "paleturquoise3", 0xff96cdcd }, { "paleturquoise4", 0xff668b8b }, { "palevioletred", 0xffdb7093 }, { "palevioletred1", 0xffff82ab }, { "palevioletred2", 0xffee799f }, { "palevioletred3", 0xffcd6889 }, { "palevioletred4", 0xff8b475d }, { "papayawhip", 0xffffefd5 }, { "peachpuff", 0xffffdab9 }, { "peachpuff1", 0xffffdab9 }, { "peachpuff2", 0xffeecbad }, { "peachpuff3", 0xffcdaf95 }, { "peachpuff4", 0xff8b7765 }, { "peru", 0xffCD853F }, { "pink", 0xffFFC0CB }, { "pink1", 0xffFFB5C5 }, { "pink2", 0xffEEA9B8 }, { "pink3", 0xffCD919E }, { "pink4", 0xff8B636C }, { "plum", 0xffDDA0DD }, { "plum1", 0xffFFBBFF }, { "plum2", 0xffEEAEEE }, { "plum3", 0xffCD96CD }, { "plum4", 0xff8B668B }, { "powderblue", 0xffb0e0e6 }, { "purple", 0xff800080 }, { "purple", 0xffA020F0 }, { "purple1", 0xff9B30FF }, { "purple2", 0xff912CEE }, { "purple3", 0xff7D26CD }, { "purple4", 0xff551A8B }, { "red", 0xffFF0000 }, { "red1", 0xffFF0000 }, { "red2", 0xffEE0000 }, { "red3", 0xffCD0000 }, { "red4", 0xff8B0000 }, { "rosybrown", 0xffbc8f8f }, { "rosybrown1", 0xffffc1c1 }, { "rosybrown2", 0xffeeb4b4 }, { "rosybrown3", 0xffcd9b9b }, { "rosybrown4", 0xff8b6969 }, { "royalblue", 0xff4169e1 }, { "royalblue1", 0xff4876ff }, { "royalblue2", 0xff436eee }, { "royalblue3", 0xff3a5fcd }, { "royalblue4", 0xff27408b }, { "saddlebrown", 0xff8b4513 }, { "salmon", 0xffFA8072 }, { "salmon1", 0xffFF8C69 }, { "salmon2", 0xffEE8262 }, { "salmon3", 0xffCD7054 }, { "salmon4", 0xff8B4C39 }, { "sandybrown", 0xfff4a460 }, { "seagreen", 0xff2e8b57 }, { "seagreen1", 0xff54ff9f }, { "seagreen2", 0xff4eee94 }, { "seagreen3", 0xff43cd80 }, { "seagreen4", 0xff2e8b57 }, { "seashell", 0xffFFF5EE }, { "seashell1", 0xffFFF5EE }, { "seashell2", 0xffEEE5DE }, { "seashell3", 0xffCDC5BF }, { "seashell4", 0xff8B8682 }, { "sienna", 0xffA0522D }, { "sienna1", 0xffFF8247 }, { "sienna2", 0xffEE7942 }, { "sienna3", 0xffCD6839 }, { "sienna4", 0xff8B4726 }, { "silver", 0xffC0C0C0 }, { "skyblue", 0xff87ceeb }, { "skyblue1", 0xff87ceff }, { "skyblue2", 0xff7ec0ee }, { "skyblue3", 0xff6ca6cd }, { "skyblue4", 0xff4a708b }, { "slateblue", 0xff6a5acd }, { "slateblue1", 0xff836fff }, { "slateblue2", 0xff7a67ee }, { "slateblue3", 0xff6959cd }, { "slateblue4", 0xff473c8b }, { "slategray", 0xff708090 }, { "slategray1", 0xffc6e2ff }, { "slategray2", 0xffb9d3ee }, { "slategray3", 0xff9fb6cd }, { "slategray4", 0xff6c7b8b }, { "slategrey", 0xff708090 }, { "snow", 0xffFFFAFA }, { "snow1", 0xffFFFAFA }, { "snow2", 0xffEEE9E9 }, { "snow3", 0xffCDC9C9 }, { "snow4", 0xff8B8989 }, { "springgreen", 0xff00ff7f }, { "springgreen1", 0xff00ff7f }, { "springgreen2", 0xff00ee76 }, { "springgreen3", 0xff00cd66 }, { "springgreen4", 0xff008b45 }, { "steelblue", 0xff4682b4 }, { "steelblue1", 0xff63b8ff }, { "steelblue2", 0xff5cacee }, { "steelblue3", 0xff4f94cd }, { "steelblue4", 0xff36648b }, { "tan", 0xffD2B48C }, { "tan1", 0xffFFA54F }, { "tan2", 0xffEE9A49 }, { "tan3", 0xffCD853F }, { "tan4", 0xff8B5A2B }, { "teal", 0xff008080 }, { "thistle", 0xffD8BFD8 }, { "thistle1", 0xffFFE1FF }, { "thistle2", 0xffEED2EE }, { "thistle3", 0xffCDB5CD }, { "thistle4", 0xff8B7B8B }, { "tomato", 0xffFF6347 }, { "tomato1", 0xffFF6347 }, { "tomato2", 0xffEE5C42 }, { "tomato3", 0xffCD4F39 }, { "tomato4", 0xff8B3626 }, { "transparent", 0xff0000FF }, { "turquoise", 0xff40E0D0 }, { "turquoise1", 0xff00F5FF }, { "turquoise2", 0xff00E5EE }, { "turquoise3", 0xff00C5CD }, { "turquoise4", 0xff00868B }, { "violet", 0xffEE82EE }, { "violetred", 0xffd02090 }, { "violetred1", 0xffff3e96 }, { "violetred2", 0xffee3a8c }, { "violetred3", 0xffcd3278 }, { "violetred4", 0xff8b2252 }, { "wheat", 0xffF5DEB3 }, { "wheat1", 0xffFFE7BA }, { "wheat2", 0xffEED8AE }, { "wheat3", 0xffCDBA96 }, { "wheat4", 0xff8B7E66 }, { "white", 0xffFFFFFF }, { "whitesmoke", 0xfff5f5f5 }, { "yellow", 0xffFFFF00 }, { "yellow1", 0xffFFFF00 }, { "yellow2", 0xffEEEE00 }, { "yellow3", 0xffCDCD00 }, { "yellow4", 0xff8B8B00 }, { "yellowgreen", 0xff9acd32 }, #endif /* EXTENDED_XPM_COLORS */ }; if (spec[0] == '#') { char buf[7]; switch(speclen) { case 4: buf[0] = buf[1] = spec[1]; buf[2] = buf[3] = spec[2]; buf[4] = buf[5] = spec[3]; break; case 7: SDL_memcpy(buf, spec + 1, 6); break; case 13: buf[0] = spec[1]; buf[1] = spec[2]; buf[2] = spec[5]; buf[3] = spec[6]; buf[4] = spec[9]; buf[5] = spec[10]; break; } buf[6] = '\0'; *argb = 0xff000000 | (Uint32)SDL_strtol(buf, NULL, 16); return 1; } else { int i; for (i = 0; i < SDL_arraysize(known); i++) { if (SDL_strncasecmp(known[i].name, spec, speclen) == 0) { *argb = known[i].argb; return 1; } } return 0; } } static char *linebuf; static int buflen; static char *error; /* * Read next line from the source. * If len > 0, it's assumed to be at least len chars (for efficiency). * Return NULL and set error upon EOF or parse error. */ static char *get_next_line(char ***lines, SDL_RWops *src, int len) { char *linebufnew; if (lines) { return *(*lines)++; } else { char c; int n; do { if (!SDL_RWread(src, &c, 1, 1)) { error = "Premature end of data"; return NULL; } } while (c != '"'); if (len) { len += 4; /* "\",\n\0" */ if (len > buflen){ buflen = len; linebufnew = (char *)SDL_realloc(linebuf, buflen); if (!linebufnew) { SDL_free(linebuf); error = "Out of memory"; return NULL; } linebuf = linebufnew; } if (!SDL_RWread(src, linebuf, len - 1, 1)) { error = "Premature end of data"; return NULL; } n = len - 2; } else { n = 0; do { if (n >= buflen - 1) { if (buflen == 0) buflen = 16; buflen *= 2; linebufnew = (char *)SDL_realloc(linebuf, buflen); if (!linebufnew) { SDL_free(linebuf); error = "Out of memory"; return NULL; } linebuf = linebufnew; } if (!SDL_RWread(src, linebuf + n, 1, 1)) { error = "Premature end of data"; return NULL; } } while (linebuf[n++] != '"'); n--; } linebuf[n] = '\0'; return linebuf; } } #define SKIPSPACE(p) \ do { \ while (SDL_isspace((unsigned char)*(p))) \ ++(p); \ } while (0) #define SKIPNONSPACE(p) \ do { \ while (!SDL_isspace((unsigned char)*(p)) && *p) \ ++(p); \ } while (0) /* read XPM from either array or RWops */ static SDL_Surface *load_xpm(char **xpm, SDL_RWops *src, SDL_bool force_32bit) { Sint64 start = 0; SDL_Surface *image = NULL; int index; int x, y; int w, h, ncolors, cpp; int indexed; Uint8 *dst; struct color_hash *colors = NULL; SDL_Color *im_colors = NULL; char *keystrings = NULL, *nextkey; char *line; char ***xpmlines = NULL; int pixels_len; error = NULL; linebuf = NULL; buflen = 0; if (src) start = SDL_RWtell(src); if (xpm) xpmlines = &xpm; line = get_next_line(xpmlines, src, 0); if (!line) goto done; /* * The header string of an XPMv3 image has the format * * [ ] * * where the hotspot coords are intended for mouse cursors. * Right now we don't use the hotspots but it should be handled * one day. */ if (SDL_sscanf(line, "%d %d %d %d", &w, &h, &ncolors, &cpp) != 4 || w <= 0 || h <= 0 || ncolors <= 0 || cpp <= 0) { error = "Invalid format description"; goto done; } /* Check for allocation overflow */ if ((size_t)(ncolors * cpp)/cpp != ncolors) { error = "Invalid color specification"; goto done; } keystrings = (char *)SDL_malloc(ncolors * cpp); if (!keystrings) { error = "Out of memory"; goto done; } nextkey = keystrings; /* Create the new surface */ if (ncolors <= 256 && !force_32bit) { indexed = 1; image = SDL_CreateRGBSurfaceWithFormat(0, w, h, 0, SDL_PIXELFORMAT_INDEX8); im_colors = image->format->palette->colors; image->format->palette->ncolors = ncolors; } else { indexed = 0; image = SDL_CreateRGBSurfaceWithFormat(0, w, h, 0, SDL_PIXELFORMAT_ARGB8888); } if (!image) { /* Hmm, some SDL error (out of memory?) */ goto done; } /* Read the colors */ colors = create_colorhash(ncolors); if (!colors) { error = "Out of memory"; goto done; } for (index = 0; index < ncolors; ++index ) { char *p; line = get_next_line(xpmlines, src, 0); if (!line) goto done; p = line + cpp + 1; /* parse a colour definition */ for (;;) { char nametype; char *colname; Uint32 argb, pixel; SKIPSPACE(p); if (!*p) { error = "colour parse error"; goto done; } nametype = *p; SKIPNONSPACE(p); SKIPSPACE(p); colname = p; SKIPNONSPACE(p); if (nametype == 's') continue; /* skip symbolic colour names */ if (!color_to_argb(colname, (int)(p - colname), &argb)) continue; SDL_memcpy(nextkey, line, cpp); if (indexed) { SDL_Color *c = im_colors + index; c->a = (Uint8)(argb >> 24); c->r = (Uint8)(argb >> 16); c->g = (Uint8)(argb >> 8); c->b = (Uint8)(argb); pixel = index; if (argb == 0x00000000) { SDL_SetColorKey(image, SDL_TRUE, pixel); } } else { pixel = argb; } add_colorhash(colors, nextkey, cpp, pixel); nextkey += cpp; break; } } /* Read the pixels */ pixels_len = w * cpp; dst = (Uint8 *)image->pixels; for (y = 0; y < h; y++) { line = get_next_line(xpmlines, src, pixels_len); if (!line) goto done; if (indexed) { /* optimization for some common cases */ if (cpp == 1) for (x = 0; x < w; x++) dst[x] = (Uint8)QUICK_COLORHASH(colors, line + x); else for (x = 0; x < w; x++) dst[x] = (Uint8)get_colorhash(colors, line + x * cpp, cpp); } else { for (x = 0; x < w; x++) ((Uint32*)dst)[x] = get_colorhash(colors, line + x * cpp, cpp); } dst += image->pitch; } done: if (error) { if ( src ) SDL_RWseek(src, start, RW_SEEK_SET); if ( image ) { SDL_FreeSurface(image); image = NULL; } IMG_SetError("%s", error); } if (keystrings) SDL_free(keystrings); free_colorhash(colors); if (linebuf) SDL_free(linebuf); return(image); } /* Load a XPM type image from an RWops datasource */ SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src) { if ( !src ) { /* The error message has been set in SDL_RWFromFile */ return NULL; } return load_xpm(NULL, src, 0); } SDL_Surface *IMG_ReadXPMFromArray(char **xpm) { if (!xpm) { IMG_SetError("array is NULL"); return NULL; } return load_xpm(xpm, NULL, SDL_FALSE); } SDL_Surface *IMG_ReadXPMFromArrayToRGB888(char **xpm) { if (!xpm) { IMG_SetError("array is NULL"); return NULL; } return load_xpm(xpm, NULL, SDL_TRUE /* force_32bit */); } #else /* not LOAD_XPM */ #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif /* See if an image is contained in a data source */ int IMG_isXPM(SDL_RWops *src) { return(0); } /* Load a XPM type image from an SDL datasource */ SDL_Surface *IMG_LoadXPM_RW(SDL_RWops *src) { return(NULL); } SDL_Surface *IMG_ReadXPMFromArray(char **xpm) { return NULL; } SDL_Surface *IMG_ReadXPMFromArrayToRGB888(char **xpm) { return NULL; } #endif /* not LOAD_XPM */ SDL2_image-2.8.8/src/IMG_xv.c0000664000000000000000000001017214751445211012420 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This is a XV thumbnail image file loading framework */ #include "SDL_image.h" #ifdef LOAD_XV static int get_line(SDL_RWops *src, char *line, int size) { while ( size > 0 ) { if ( !SDL_RWread(src, line, 1, 1) ) { return -1; } if ( *line == '\r' ) { continue; } if ( *line == '\n' ) { *line = '\0'; return 0; } ++line; --size; } /* Out of space for the line */ return -1; } static int get_header(SDL_RWops *src, int *w, int *h) { char line[1024]; *w = 0; *h = 0; /* Check the header magic */ if ( (get_line(src, line, sizeof(line)) < 0) || (SDL_memcmp(line, "P7 332", 6) != 0) ) { return -1; } /* Read the header */ while ( get_line(src, line, sizeof(line)) == 0 ) { if ( SDL_memcmp(line, "#BUILTIN:", 9) == 0 ) { /* Builtin image, no data */ break; } if ( SDL_memcmp(line, "#END_OF_COMMENTS", 16) == 0 ) { if ( get_line(src, line, sizeof(line)) == 0 ) { SDL_sscanf(line, "%d %d", w, h); if ( *w >= 0 && *h >= 0 ) { return 0; } } break; } } /* No image data */ return -1; } /* See if an image is contained in a data source */ int IMG_isXV(SDL_RWops *src) { Sint64 start; int is_XV; int w, h; if ( !src ) return 0; start = SDL_RWtell(src); is_XV = 0; if ( get_header(src, &w, &h) == 0 ) { is_XV = 1; } SDL_RWseek(src, start, RW_SEEK_SET); return(is_XV); } /* Load a XV thumbnail image from an SDL datasource */ SDL_Surface *IMG_LoadXV_RW(SDL_RWops *src) { Sint64 start; const char *error = NULL; SDL_Surface *surface = NULL; int w, h; Uint8 *pixels; if ( !src ) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); /* Read the header */ if ( get_header(src, &w, &h) < 0 ) { error = "Unsupported image format"; goto done; } /* Create the 3-3-2 indexed palette surface */ surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 0, SDL_PIXELFORMAT_RGB332); if ( surface == NULL ) { error = "Out of memory"; goto done; } /* Load the image data */ for ( pixels = (Uint8 *)surface->pixels; h > 0; --h ) { if ( !SDL_RWread(src, pixels, w, 1) ) { error = "Couldn't read image data"; goto done; } pixels += surface->pitch; } done: if ( error ) { SDL_RWseek(src, start, RW_SEEK_SET); if ( surface ) { SDL_FreeSurface(surface); surface = NULL; } IMG_SetError("%s", error); } return surface; } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif /* See if an image is contained in a data source */ int IMG_isXV(SDL_RWops *src) { return(0); } /* Load a XXX type image from an SDL datasource */ SDL_Surface *IMG_LoadXV_RW(SDL_RWops *src) { return(NULL); } #endif /* LOAD_XV */ SDL2_image-2.8.8/src/IMG_xxx.c0000664000000000000000000000430714751445211012615 0ustar00/* SDL_image: An example image loading library for use with SDL Copyright (C) 1997-2025 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ /* This is a generic "format not supported" image framework */ #include "SDL_image.h" #ifdef LOAD_XXX /* See if an image is contained in a data source */ int IMG_isXXX(SDL_RWops *src) { int start; int is_XXX; if (!src) { return 0; } start = SDL_RWtell(src); is_XXX = 0; /* Detect the image here */ SDL_RWseek(src, start, RW_SEEK_SET); return is_XXX; } /* Load an XXX type image from an SDL datasource */ SDL_Surface *IMG_LoadXXX_RW(SDL_RWops *src) { int start; const char *error = NULL; SDL_Surface *surface = NULL; if (!src) { /* The error message has been set in SDL_RWFromFile */ return NULL; } start = SDL_RWtell(src); /* Load the image here */ if (error) { SDL_RWseek(src, start, RW_SEEK_SET); if (surface) { SDL_FreeSurface(surface); surface = NULL; } IMG_SetError("%s", error); } return surface; } #else #if _MSC_VER >= 1300 #pragma warning(disable : 4100) /* warning C4100: 'op' : unreferenced formal parameter */ #endif int IMG_isXXX(SDL_RWops *src) { (void) src; return 0; } SDL_Surface *IMG_LoadXXX_RW(SDL_RWops *src) { (void) src; return NULL; } #endif /* LOAD_XXX */ SDL2_image-2.8.8/src/miniz.h0000664000000000000000000070066314444663624012446 0ustar00/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). * Change History 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!): - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti - Merged MZ_FORCEINLINE fix from hdeanclark - Fix include before config #ifdef, thanks emil.brink - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can set it to 1 for real-time compression). - Merged in some compiler fixes from paulharris's github repro. - Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. - Added example6.c, which dumps an image of the mandelbrot set to a PNG file. - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. 5/28/11 v1.11 - Added statement from unlicense.org 5/27/11 v1.10 - Substantial compressor optimizations: - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. - Refactored the compression code for better readability and maintainability. - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large drop in throughput on some files). 5/15/11 v1.09 - Initial stable release. * Low-level Deflate/Inflate implementation notes: Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses approximately as well as zlib. Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory block large enough to hold the entire file. The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. * zlib-style API notes: miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in zlib replacement in many apps: The z_stream struct, optional memory allocation callbacks deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound inflateInit/inflateInit2/inflate/inflateEnd compress, compress2, compressBound, uncompress CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. Supports raw deflate streams or standard zlib streams with adler-32 checking. Limitations: The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but there are no guarantees that miniz.c pulls this off perfectly. * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by Alex Evans. Supports 1-4 bytes/pixel images. * ZIP archive API notes: The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to get the job done with minimal fuss. There are simple API's to retrieve file information, read files from existing archives, create new archives, append new files to existing archives, or clone archive data from one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), or you can specify custom file read/write callbacks. - Archive reading: Just call this function to read a single file from a disk archive: void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); The locate operation can optionally check file comments too, which (as one example) can be used to identify multiple versions of the same file in an archive. This function uses a simple linear search through the central directory, so it's not very fast. Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data to disk and builds an exact image of the central directory in memory. The central directory image is written all at once at the end of the archive file when the archive is finalized. The archive writer can optionally align each file's local header and file data to any power of 2 alignment, which can be useful when the archive will be read from optical media. Also, the writer supports placing arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still readable by any ZIP tool. - Archive appending: The simple way to add a single file to an archive is to call this function: mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); The archive will be created if it doesn't already exist, otherwise it'll be appended to. Note the appending is done in-place and is not an atomic operation, so if something goes wrong during the operation it's possible the archive could be left without a central directory (although the local file headers and file data will be fine, so the archive will be recoverable). For more complex archive modification scenarios: 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and you're done. This is safe but requires a bunch of temporary disk space or heap memory. 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), append new files as needed, then finalize the archive which will write an updated central directory to the original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - ZIP archive support limitations: No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. Requires streams capable of seeking. * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. * Important: For best perf. be sure to customize the below macros for your target platform: #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_LITTLE_ENDIAN 1 #define MINIZ_HAS_64BIT_REGISTERS 1 * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). */ #ifndef MINIZ_HEADER_INCLUDED #define MINIZ_HEADER_INCLUDED /*#include */ // Defines to completely disable specific portions of miniz.c: // If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. // Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. #define MINIZ_NO_STDIO // If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or // get/set file times, and the C run-time funcs that get/set times won't be called. // The current downside is the times written to your archives will be from 1979. #define MINIZ_NO_TIME // Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. //#define MINIZ_NO_DEFLATE_APIS // Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. #define MINIZ_NO_INFLATE_APIS // Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. #define MINIZ_NO_ARCHIVE_APIS // Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. #define MINIZ_NO_ARCHIVE_WRITING_APIS // Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. #define MINIZ_NO_ZLIB_APIS // Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. #define MINIZ_NO_ZLIB_COMPATIBLE_NAMES // Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. // Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc // callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user // functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. //#define MINIZ_NO_MALLOC #define MINIZ_SDL_MALLOC #ifdef MINIZ_NO_INFLATE_APIS #define MINIZ_NO_ARCHIVE_APIS #endif #ifdef MINIZ_NO_DEFLATE_APIS #define MINIZ_NO_ARCHIVE_WRITING_APIS #endif // Define MINIZ_STATIC_FUNCTIONS to make all functions static. You probably // want this if you're using miniz in a library #define MINIZ_STATIC_FUNCTIONS #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux #define MINIZ_NO_TIME #endif #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) #include #endif #if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) // MINIZ_X86_OR_X64_CPU is only used to help set the below macros. #define MINIZ_X86_OR_X64_CPU 1 #endif #ifndef MINIZ_LITTLE_ENDIAN /* if not defined by SDL */ #if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU // Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. #define MINIZ_LITTLE_ENDIAN 1 #endif #endif /**/ #ifndef MINIZ_USE_UNALIGNED_LOADS_AND_STORES #if MINIZ_X86_OR_X64_CPU // Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #endif #endif /**/ #if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) // Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). #define MINIZ_HAS_64BIT_REGISTERS 1 #endif #ifdef MINIZ_STATIC_FUNCTIONS #define MINIZ_STATIC static #else #define MINIZ_STATIC #endif #ifdef __cplusplus extern "C" { #endif // ------------------- zlib-style API Definitions. // For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! typedef unsigned long mz_ulong; // mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. MINIZ_STATIC void mz_free(void *p); #define MZ_ADLER32_INIT (1) // mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. MINIZ_STATIC mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); #define MZ_CRC32_INIT (0) // mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. MINIZ_STATIC mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); // Compression strategies. enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; // Method #define MZ_DEFLATED 8 #ifndef MINIZ_NO_ZLIB_APIS // Heap allocation callbacks. // Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); typedef void (*mz_free_func)(void *opaque, void *address); typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); #define MZ_VERSION "9.1.15" #define MZ_VERNUM 0x91F0 #define MZ_VER_MAJOR 9 #define MZ_VER_MINOR 1 #define MZ_VER_REVISION 15 #define MZ_VER_SUBREVISION 0 // Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; // Return status codes. MZ_PARAM_ERROR is non-standard. enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; // Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; // Window bits #define MZ_DEFAULT_WINDOW_BITS 15 struct mz_internal_state; // Compression/decompression stream struct. typedef struct mz_stream_s { const unsigned char *next_in; // pointer to next byte to read unsigned int avail_in; // number of bytes available at next_in mz_ulong total_in; // total number of bytes consumed so far unsigned char *next_out; // pointer to next byte to write unsigned int avail_out; // number of bytes that can be written to next_out mz_ulong total_out; // total number of bytes produced so far char *msg; // error msg (unused) struct mz_internal_state *state; // internal state, allocated by zalloc/zfree mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) mz_free_func zfree; // optional heap free function (defaults to free) void *opaque; // heap alloc function user pointer int data_type; // data_type (unused) mz_ulong adler; // adler32 of the source or uncompressed data mz_ulong reserved; // not used } mz_stream; typedef mz_stream *mz_streamp; // Returns the version string of miniz.c. MINIZ_STATIC const char *mz_version(void); #ifndef MINIZ_NO_DEFLATE_APIS // mz_deflateInit() initializes a compressor with default options: // Parameters: // pStream must point to an initialized mz_stream struct. // level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. // level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. // (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) // Return values: // MZ_OK on success. // MZ_STREAM_ERROR if the stream is bogus. // MZ_PARAM_ERROR if the input parameters are bogus. // MZ_MEM_ERROR on out of memory. MINIZ_STATIC int mz_deflateInit(mz_streamp pStream, int level); // mz_deflateInit2() is like mz_deflate(), except with more control: // Additional parameters: // method must be MZ_DEFLATED // window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) // mem_level must be between [1, 9] (it's checked but ignored by miniz.c) MINIZ_STATIC int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); // Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). MINIZ_STATIC int mz_deflateReset(mz_streamp pStream); // mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. // Parameters: // pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. // flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. // Return values: // MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). // MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. // MZ_STREAM_ERROR if the stream is bogus. // MZ_PARAM_ERROR if one of the parameters is invalid. // MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) MINIZ_STATIC int mz_deflate(mz_streamp pStream, int flush); // mz_deflateEnd() deinitializes a compressor: // Return values: // MZ_OK on success. // MZ_STREAM_ERROR if the stream is bogus. MINIZ_STATIC int mz_deflateEnd(mz_streamp pStream); // mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. MINIZ_STATIC mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); // Single-call compression functions mz_compress() and mz_compress2(): // Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. MINIZ_STATIC int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); MINIZ_STATIC int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); // mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). MINIZ_STATIC mz_ulong mz_compressBound(mz_ulong source_len); #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #ifndef MINIZ_NO_INFLATE_APIS // Initializes a decompressor. MINIZ_STATIC int mz_inflateInit(mz_streamp pStream); // mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: // window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). MINIZ_STATIC int mz_inflateInit2(mz_streamp pStream, int window_bits); // Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. // Parameters: // pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. // flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. // On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). // MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. // Return values: // MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. // MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. // MZ_STREAM_ERROR if the stream is bogus. // MZ_DATA_ERROR if the deflate stream is invalid. // MZ_PARAM_ERROR if one of the parameters is invalid. // MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again // with more input data, or with more room in the output buffer (except when using single call decompression, described above). MINIZ_STATIC int mz_inflate(mz_streamp pStream, int flush); // Deinitializes a decompressor. MINIZ_STATIC int mz_inflateEnd(mz_streamp pStream); // Single-call decompression. // Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. MINIZ_STATIC int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ // Returns a string description of the specified error code, or NULL if the error code is invalid. MINIZ_STATIC const char *mz_error(int err); // Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. // Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES typedef unsigned char Byte; typedef unsigned int uInt; typedef mz_ulong uLong; typedef Byte Bytef; typedef uInt uIntf; typedef char charf; typedef int intf; typedef void *voidpf; typedef uLong uLongf; typedef void *voidp; typedef void *const voidpc; #define Z_NULL 0 #define Z_NO_FLUSH MZ_NO_FLUSH #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH #define Z_SYNC_FLUSH MZ_SYNC_FLUSH #define Z_FULL_FLUSH MZ_FULL_FLUSH #define Z_FINISH MZ_FINISH #define Z_BLOCK MZ_BLOCK #define Z_OK MZ_OK #define Z_STREAM_END MZ_STREAM_END #define Z_NEED_DICT MZ_NEED_DICT #define Z_ERRNO MZ_ERRNO #define Z_STREAM_ERROR MZ_STREAM_ERROR #define Z_DATA_ERROR MZ_DATA_ERROR #define Z_MEM_ERROR MZ_MEM_ERROR #define Z_BUF_ERROR MZ_BUF_ERROR #define Z_VERSION_ERROR MZ_VERSION_ERROR #define Z_PARAM_ERROR MZ_PARAM_ERROR #define Z_NO_COMPRESSION MZ_NO_COMPRESSION #define Z_BEST_SPEED MZ_BEST_SPEED #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY #define Z_FILTERED MZ_FILTERED #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY #define Z_RLE MZ_RLE #define Z_FIXED MZ_FIXED #define Z_DEFLATED MZ_DEFLATED #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS #define alloc_func mz_alloc_func #define free_func mz_free_func #define internal_state mz_internal_state #define z_stream mz_stream #ifndef MINIZ_NO_DEFLATE_APIS #define deflateInit mz_deflateInit #define deflateInit2 mz_deflateInit2 #define deflateReset mz_deflateReset #define deflate mz_deflate #define deflateEnd mz_deflateEnd #define deflateBound mz_deflateBound #define compress mz_compress #define compress2 mz_compress2 #define compressBound mz_compressBound #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #ifndef MINIZ_NO_INFLATE_APIS #define inflateInit mz_inflateInit #define inflateInit2 mz_inflateInit2 #define inflate mz_inflate #define inflateEnd mz_inflateEnd #define uncompress mz_uncompress #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ #define crc32 mz_crc32 #define adler32 mz_adler32 #define MAX_WBITS 15 #define MAX_MEM_LEVEL 9 #define zError mz_error #define ZLIB_VERSION MZ_VERSION #define ZLIB_VERNUM MZ_VERNUM #define ZLIB_VER_MAJOR MZ_VER_MAJOR #define ZLIB_VER_MINOR MZ_VER_MINOR #define ZLIB_VER_REVISION MZ_VER_REVISION #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION #define zlibVersion mz_version #define zlib_version mz_version() #endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES #endif // MINIZ_NO_ZLIB_APIS // ------------------- Types and macros typedef unsigned char mz_uint8; typedef signed short mz_int16; typedef unsigned short mz_uint16; typedef unsigned int mz_uint32; typedef unsigned int mz_uint; typedef long long mz_int64; typedef unsigned long long mz_uint64; typedef int mz_bool; #define MZ_FALSE (0) #define MZ_TRUE (1) // An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message. #ifdef _MSC_VER #define MZ_MACRO_END while (0, 0) #else #define MZ_MACRO_END while (0) #endif // ------------------- ZIP archive reading/writing #ifndef MINIZ_NO_ARCHIVE_APIS enum { MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 }; typedef struct { mz_uint32 m_file_index; mz_uint32 m_central_dir_ofs; mz_uint16 m_version_made_by; mz_uint16 m_version_needed; mz_uint16 m_bit_flag; mz_uint16 m_method; #ifndef MINIZ_NO_TIME time_t m_time; #endif mz_uint32 m_crc32; mz_uint64 m_comp_size; mz_uint64 m_uncomp_size; mz_uint16 m_internal_attr; mz_uint32 m_external_attr; mz_uint64 m_local_header_ofs; mz_uint32 m_comment_size; char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; } mz_zip_archive_file_stat; typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); struct mz_zip_internal_state_tag; typedef struct mz_zip_internal_state_tag mz_zip_internal_state; typedef enum { MZ_ZIP_MODE_INVALID = 0, MZ_ZIP_MODE_READING = 1, MZ_ZIP_MODE_WRITING = 2, MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 } mz_zip_mode; typedef struct mz_zip_archive_tag { mz_uint64 m_archive_size; mz_uint64 m_central_directory_file_ofs; mz_uint m_total_files; mz_zip_mode m_zip_mode; mz_uint m_file_offset_alignment; mz_alloc_func m_pAlloc; mz_free_func m_pFree; mz_realloc_func m_pRealloc; void *m_pAlloc_opaque; mz_file_read_func m_pRead; mz_file_write_func m_pWrite; void *m_pIO_opaque; mz_zip_internal_state *m_pState; } mz_zip_archive; typedef enum { MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 } mz_zip_flags; // ZIP archive reading // Inits a ZIP archive reader. // These functions read and validate the archive's central directory. MINIZ_STATIC mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); MINIZ_STATIC mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); #ifndef MINIZ_NO_STDIO MINIZ_STATIC mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); #endif // Returns the total number of files in the archive. MINIZ_STATIC mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); // Returns detailed information about an archive file entry. MINIZ_STATIC mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); // Determines if an archive file entry is a directory entry. MINIZ_STATIC mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); MINIZ_STATIC mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); // Retrieves the filename of an archive file entry. // Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. MINIZ_STATIC mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); // Attempts to locates a file in the archive's central directory. // Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH // Returns -1 if the file cannot be found. MINIZ_STATIC int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); // Extracts a archive file to a memory buffer using no memory allocation. MINIZ_STATIC mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); MINIZ_STATIC mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); // Extracts a archive file to a memory buffer. MINIZ_STATIC mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); MINIZ_STATIC mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); // Extracts a archive file to a dynamically allocated heap buffer. MINIZ_STATIC void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); MINIZ_STATIC void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); // Extracts a archive file using a callback function to output the file's data. MINIZ_STATIC mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); MINIZ_STATIC mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); #ifndef MINIZ_NO_STDIO // Extracts a archive file to a disk file and sets its last accessed and modified times. // This function only extracts files, not archive directory records. MINIZ_STATIC mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); MINIZ_STATIC mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); #endif // Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. MINIZ_STATIC mz_bool mz_zip_reader_end(mz_zip_archive *pZip); // ZIP archive writing #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS // Inits a ZIP archive writer. MINIZ_STATIC mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); MINIZ_STATIC mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); #ifndef MINIZ_NO_STDIO MINIZ_STATIC mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); #endif // Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. // For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. // For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). // Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. // Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before // the archive is finalized the file's central directory will be hosed. MINIZ_STATIC mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); // Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. // To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. // level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. MINIZ_STATIC mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); MINIZ_STATIC mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); #ifndef MINIZ_NO_STDIO // Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. // level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. MINIZ_STATIC mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); #endif // Adds a file to an archive by fully cloning the data from another archive. // This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. MINIZ_STATIC mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); // Finalizes the archive by writing the central directory records followed by the end of central directory record. // After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). // An archive must be manually finalized by calling this function for it to be valid. MINIZ_STATIC mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); MINIZ_STATIC mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); // Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. // Note for the archive to be valid, it must have been finalized before ending. MINIZ_STATIC mz_bool mz_zip_writer_end(mz_zip_archive *pZip); // Misc. high-level helper functions: // mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. // level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. MINIZ_STATIC mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); // Reads a single file from an archive into a heap block. // Returns NULL on failure. MINIZ_STATIC void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); #endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS #endif // #ifndef MINIZ_NO_ARCHIVE_APIS // ------------------- Low-level Decompression API Definitions #ifndef MINIZ_NO_INFLATE_APIS // Decompression flags used by tinfl_decompress(). // TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. // TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. // TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). // TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. enum { TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_HAS_MORE_INPUT = 2, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, TINFL_FLAG_COMPUTE_ADLER32 = 8 }; // High level decompression functions: // tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). // On entry: // pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. // On return: // Function returns a pointer to the decompressed data, or NULL on failure. // *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. // The caller must call mz_free() on the returned block when it's no longer needed. MINIZ_STATIC void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); // tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. // Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) MINIZ_STATIC size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); // tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. // Returns 1 on success or 0 on failure. typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); MINIZ_STATIC int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; // Max size of LZ dictionary. #define TINFL_LZ_DICT_SIZE 32768 // Return status. typedef enum { TINFL_STATUS_BAD_PARAM = -3, TINFL_STATUS_ADLER32_MISMATCH = -2, TINFL_STATUS_FAILED = -1, TINFL_STATUS_DONE = 0, TINFL_STATUS_NEEDS_MORE_INPUT = 1, TINFL_STATUS_HAS_MORE_OUTPUT = 2 } tinfl_status; // Initializes the decompressor to its initial state. #define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END #define tinfl_get_adler32(r) (r)->m_check_adler32 // Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. // This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. MINIZ_STATIC tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); // Internal/private bits follow. enum { TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; typedef struct { mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; } tinfl_huff_table; #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #endif #if TINFL_USE_64BIT_BITBUF typedef mz_uint64 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (64) #else typedef mz_uint32 tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (32) #endif struct tinfl_decompressor_tag { mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ // ------------------- Low-level Compression API Definitions #ifndef MINIZ_NO_DEFLATE_APIS // Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). #define TDEFL_LESS_MEMORY 0 // tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): // TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). enum { TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF }; // TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. // TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). // TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. // TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). // TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) // TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. // TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. // TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. // The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). enum { TDEFL_WRITE_ZLIB_HEADER = 0x01000, TDEFL_COMPUTE_ADLER32 = 0x02000, TDEFL_GREEDY_PARSING_FLAG = 0x04000, TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, TDEFL_RLE_MATCHES = 0x10000, TDEFL_FILTER_MATCHES = 0x20000, TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 }; #ifndef MINIZ_SDL_NOUNUSED // High level compression functions: // tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). // On entry: // pSrc_buf, src_buf_len: Pointer and size of source block to compress. // flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. // On return: // Function returns a pointer to the compressed data, or NULL on failure. // *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. // The caller must free() the returned block when it's no longer needed. MINIZ_STATIC void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); // tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. // Returns 0 on failure. MINIZ_STATIC size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); #endif /* MINIZ_SDL_NOUNUSED */ // Compresses an image to a compressed PNG file in memory. // On entry: // pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. // The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. // level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL // If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). // On return: // Function returns a pointer to the compressed data, or NULL on failure. // *pLen_out will be set to the size of the PNG image file. // The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. MINIZ_STATIC void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, int bpl, size_t *pLen_out, mz_uint level, mz_bool flip); MINIZ_STATIC void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, int bpl, size_t *pLen_out); // Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); #ifndef MINIZ_SDL_NOUNUSED // tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. MINIZ_STATIC mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); #endif //enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; #define TDEFL_MAX_HUFF_TABLES 3 #define TDEFL_MAX_HUFF_SYMBOLS_0 288 #define TDEFL_MAX_HUFF_SYMBOLS_1 32 #define TDEFL_MAX_HUFF_SYMBOLS_2 19 #define TDEFL_LZ_DICT_SIZE 32768 #define TDEFL_LZ_DICT_SIZE_MASK ( TDEFL_LZ_DICT_SIZE - 1 ) #define TDEFL_MIN_MATCH_LEN 3 #define TDEFL_MAX_MATCH_LEN 258 // TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). #if TDEFL_LESS_MEMORY enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #else enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #endif // The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. typedef enum { TDEFL_STATUS_BAD_PARAM = -2, TDEFL_STATUS_PUT_BUF_FAILED = -1, TDEFL_STATUS_OKAY = 0, TDEFL_STATUS_DONE = 1, } tdefl_status; // Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums typedef enum { TDEFL_NO_FLUSH = 0, TDEFL_SYNC_FLUSH = 2, TDEFL_FULL_FLUSH = 3, TDEFL_FINISH = 4 } tdefl_flush; // tdefl's compression state structure. typedef struct { tdefl_put_buf_func_ptr m_pPut_buf_func; void *m_pPut_buf_user; mz_uint m_flags, m_max_probes[2]; int m_greedy_parsing; mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; tdefl_status m_prev_return_status; const void *m_pIn_buf; void *m_pOut_buf; size_t *m_pIn_buf_size, *m_pOut_buf_size; tdefl_flush m_flush; const mz_uint8 *m_pSrc; size_t m_src_buf_left, m_out_buf_ofs; mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; } tdefl_compressor; // Initializes the compressor. // There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. // pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. // If pBut_buf_func is NULL the user should always call the tdefl_compress() API. // flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) MINIZ_STATIC tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); // Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. MINIZ_STATIC tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); // tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. // tdefl_compress_buffer() always consumes the entire input buffer. MINIZ_STATIC tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); #ifndef MINIZ_SDL_NOUNUSED MINIZ_STATIC tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); MINIZ_STATIC mz_uint32 tdefl_get_adler32(tdefl_compressor *d); #endif // Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. #ifndef MINIZ_NO_ZLIB_APIS // Create tdefl_compress() flags given zlib-style compression parameters. // level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) // window_bits may be -15 (raw deflate) or 15 (zlib) // strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED MINIZ_STATIC mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); #endif // #ifndef MINIZ_NO_ZLIB_APIS #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #ifdef __cplusplus } #endif #endif // MINIZ_HEADER_INCLUDED // ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) #ifndef MINIZ_HEADER_FILE_ONLY #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable:4505) // unreferenced local function has been removed #endif typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1]; typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1]; typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; /*#include */ #ifndef MZ_ASSERT #include #define MZ_ASSERT(x) assert(x) #endif #ifdef MINIZ_NO_MALLOC #define MZ_MALLOC(x) NULL #define MZ_FREE(x) (void)x, ((void)0) #define MZ_REALLOC(p, x) NULL #elif defined(MINIZ_SDL_MALLOC) #define MZ_MALLOC(x) SDL_malloc(x) #define MZ_FREE(x) SDL_free(x) #define MZ_REALLOC(p, x) SDL_realloc(p, x) #else #define MZ_MALLOC(x) malloc(x) #define MZ_FREE(x) free(x) #define MZ_REALLOC(p, x) realloc(p, x) #endif #define MZ_MAX(a,b) (((a)>(b))?(a):(b)) #define MZ_MIN(a,b) (((a)<(b))?(a):(b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) #define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) #define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) #else #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) #endif #ifdef _MSC_VER #define MZ_FORCEINLINE __forceinline #elif defined(__GNUC__) #define MZ_FORCEINLINE inline __attribute__((__always_inline__)) #else #define MZ_FORCEINLINE inline #endif #ifdef __cplusplus extern "C" { #endif // ------------------- zlib-style API's mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; if (!ptr) return MZ_ADLER32_INIT; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } return (s2 << 16) + s1; } // Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; mz_uint32 crcu32 = (mz_uint32)crc; if (!ptr) return MZ_CRC32_INIT; crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } return ~crcu32; } MINIZ_STATIC void mz_free(void *p) { MZ_FREE(p); } #ifndef MINIZ_NO_ZLIB_APIS static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } const char *mz_version(void) { return MZ_VERSION; } #ifndef MINIZ_NO_DEFLATE_APIS int mz_deflateInit(mz_streamp pStream, int level) { return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); } int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) { tdefl_compressor *pComp; mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); if (!pStream) return MZ_STREAM_ERROR; if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = MZ_ADLER32_INIT; pStream->msg = NULL; pStream->reserved = 0; pStream->total_in = 0; pStream->total_out = 0; if (!pStream->zalloc) pStream->zalloc = def_alloc_func; if (!pStream->zfree) pStream->zfree = def_free_func; pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); if (!pComp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pComp; if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { mz_deflateEnd(pStream); return MZ_PARAM_ERROR; } return MZ_OK; } int mz_deflateReset(mz_streamp pStream) { if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; pStream->total_in = pStream->total_out = 0; tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); return MZ_OK; } int mz_deflate(mz_streamp pStream, int flush) { size_t in_bytes, out_bytes; mz_ulong orig_total_in, orig_total_out; int mz_status = MZ_OK; if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; if (!pStream->avail_out) return MZ_BUF_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; for ( ; ; ) { tdefl_status defl_status; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (defl_status < 0) { mz_status = MZ_STREAM_ERROR; break; } else if (defl_status == TDEFL_STATUS_DONE) { mz_status = MZ_STREAM_END; break; } else if (!pStream->avail_out) break; else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) break; return MZ_BUF_ERROR; // Can't make forward progress without some input. } } return mz_status; } int mz_deflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { (void)pStream; // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); } int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) { int status; mz_stream stream; memset(&stream, 0, sizeof(stream)); // In case mz_ulong is 64-bits (argh I hate longs). if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_deflateInit(&stream, level); if (status != MZ_OK) return status; status = mz_deflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_deflateEnd(&stream); return (status == MZ_OK) ? MZ_BUF_ERROR : status; } *pDest_len = stream.total_out; return mz_deflateEnd(&stream); } int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); } mz_ulong mz_compressBound(mz_ulong source_len) { return mz_deflateBound(NULL, source_len); } #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #ifndef MINIZ_NO_INFLATE_APIS typedef struct { tinfl_decompressor m_decomp; mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; tinfl_status m_last_status; } inflate_state; int mz_inflateInit2(mz_streamp pStream, int window_bits) { inflate_state *pDecomp; if (!pStream) return MZ_STREAM_ERROR; if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; pStream->data_type = 0; pStream->adler = 0; pStream->msg = NULL; pStream->total_in = 0; pStream->total_out = 0; pStream->reserved = 0; if (!pStream->zalloc) pStream->zalloc = def_alloc_func; if (!pStream->zfree) pStream->zfree = def_free_func; pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); if (!pDecomp) return MZ_MEM_ERROR; pStream->state = (struct mz_internal_state *)pDecomp; tinfl_init(&pDecomp->m_decomp); pDecomp->m_dict_ofs = 0; pDecomp->m_dict_avail = 0; pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; pDecomp->m_first_call = 1; pDecomp->m_has_flushed = 0; pDecomp->m_window_bits = window_bits; return MZ_OK; } int mz_inflateInit(mz_streamp pStream) { return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); } int mz_inflate(mz_streamp pStream, int flush) { inflate_state* pState; mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; size_t in_bytes, out_bytes, orig_avail_in; tinfl_status status; if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState = (inflate_state*)pStream->state; if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; orig_avail_in = pStream->avail_in; first_call = pState->m_first_call; pState->m_first_call = 0; if (pState->m_last_status < 0) return MZ_DATA_ERROR; if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; pState->m_has_flushed |= (flush == MZ_FINISH); if ((flush == MZ_FINISH) && (first_call)) { // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; if (status < 0) return MZ_DATA_ERROR; else if (status != TINFL_STATUS_DONE) { pState->m_last_status = TINFL_STATUS_FAILED; return MZ_BUF_ERROR; } return MZ_STREAM_END; } // flush != MZ_FINISH then we must assume there's more input. if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; if (pState->m_dict_avail) { n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } for ( ; ; ) { in_bytes = pStream->avail_in; out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); pState->m_last_status = status; pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); pState->m_dict_avail = (mz_uint)out_bytes; n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); if (status < 0) return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. else if (flush == MZ_FINISH) { // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. if (status == TINFL_STATUS_DONE) return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. else if (!pStream->avail_out) return MZ_BUF_ERROR; } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) break; } return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; } int mz_inflateEnd(mz_streamp pStream) { if (!pStream) return MZ_STREAM_ERROR; if (pStream->state) { pStream->zfree(pStream->opaque, pStream->state); pStream->state = NULL; } return MZ_OK; } int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) { mz_stream stream; int status; memset(&stream, 0, sizeof(stream)); // In case mz_ulong is 64-bits (argh I hate longs). if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; status = mz_inflateInit(&stream); if (status != MZ_OK) return status; status = mz_inflate(&stream, MZ_FINISH); if (status != MZ_STREAM_END) { mz_inflateEnd(&stream); return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; } *pDest_len = stream.total_out; return mz_inflateEnd(&stream); } #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ const char *mz_error(int err) { static struct { int m_err; const char *m_pDesc; } s_error_descs[] = { { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } }; mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; return NULL; } #endif //MINIZ_NO_ZLIB_APIS // ------------------- Low-level Decompression (completely independent from all compression API's) #ifndef MINIZ_NO_INFLATE_APIS #define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) #define TINFL_MEMSET(p, c, l) memset(p, c, l) #define TINFL_CR_BEGIN switch(r->m_state) { case 0: #define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END #define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END #define TINFL_CR_FINISH } // TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never // reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. #define TINFL_GET_BYTE(state_index, c) do { \ if (pIn_buf_cur >= pIn_buf_end) { \ for ( ; ; ) { \ if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ if (pIn_buf_cur < pIn_buf_end) { \ c = *pIn_buf_cur++; \ break; \ } \ } else { \ c = 0; \ break; \ } \ } \ } else c = *pIn_buf_cur++; } MZ_MACRO_END #define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) #define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END #define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END // TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. // It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a // Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the // bit buffer contains >=15 bits (deflate's max. Huffman code size). #define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ do { \ temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) { \ code_len = temp >> 9; \ if ((code_len) && (num_bits >= code_len)) \ break; \ } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do { \ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); \ if (temp >= 0) break; \ } \ TINFL_GET_BYTE(state_index, c); \ bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ num_bits += 8; \ } while (num_bits < 15); // TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read // beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully // decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. // The slow path is only executed at the very end of the input buffer. #define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ int temp; mz_uint code_len, c; \ if (num_bits < 15) { \ if ((pIn_buf_end - pIn_buf_cur) < 2) { \ TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ } else { \ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ } \ } \ if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else { \ code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { static const int s_length_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 s_length_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 s_dist_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 s_dist_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 const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; static const int s_min_table_sizes[3] = { 257, 1, 4 }; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; TINFL_CR_BEGIN bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } } do { TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; if (r->m_type == 0) { TINFL_SKIP_BITS(5, num_bits & 7); for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } while ((counter) && (num_bits)) { TINFL_GET_BITS(51, dist, 8); while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)dist; counter--; } while (counter) { size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } while (pIn_buf_cur >= pIn_buf_end) { if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); } else { TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); } } n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; } } else if (r->m_type == 3) { TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); } else { if (r->m_type == 1) { mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; } else { for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } MZ_CLEAR_ARR(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for ( ; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; tinfl_huff_table *pTable; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_ARR(total_syms); MZ_CLEAR_ARR(pTable->m_look_up); MZ_CLEAR_ARR(pTable->m_tree); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } if ((65536 != total) && (used_syms > 1)) { TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) { mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } if ((dist == 16) && (!counter)) { TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); } num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; } if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for ( ; ; ) { mz_uint8 *pSrc; for ( ; ; ) { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (mz_uint8)counter; } else { int sym2; mz_uint code_len; #if TINFL_USE_64BIT_BITBUF if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } #else if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; bit_buf >>= code_len; num_bits -= code_len; if (counter & 256) break; #if !TINFL_USE_64BIT_BITBUF if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; num_bits -= code_len; pOut_buf_cur[0] = (mz_uint8)counter; if (sym2 & 256) { pOut_buf_cur++; counter = sym2; break; } pOut_buf_cur[1] = (mz_uint8)sym2; pOut_buf_cur += 2; } } if ((counter &= 511) == 256) break; num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); } pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { while (counter--) { while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; } continue; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES else if ((counter >= 9) && (counter <= dist)) { const mz_uint8 *pSrc_end = pSrc + (counter & ~7); do { ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; pOut_buf_cur += 8; } while ((pSrc += 8) < pSrc_end); if ((counter &= 7) < 3) { if (counter) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } continue; } } #endif do { pOut_buf_cur[0] = pSrc[0]; pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur[2] = pSrc[2]; pOut_buf_cur += 3; pSrc += 3; } while ((int)(counter -= 3) > 2); if ((int)counter > 0) { pOut_buf_cur[0] = pSrc[0]; if ((int)counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } } } } while (!(r->m_final & 1)); if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } } TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); TINFL_CR_FINISH common_exit: r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) { const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; } return status; } // Higher level helper functions. void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; *pOut_len = 0; tinfl_init(&decomp); for ( ; ; ) { size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } src_buf_ofs += src_buf_size; *pOut_len += dst_buf_size; if (status == TINFL_STATUS_DONE) break; new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); if (!pNew_buf) { MZ_FREE(pBuf); *pOut_len = 0; return NULL; } pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; } return pBuf; } size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; } int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { int result = 0; tinfl_decompressor decomp; mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; memset(pDict,0,TINFL_LZ_DICT_SIZE); tinfl_init(&decomp); for ( ; ; ) { size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); in_buf_ofs += in_buf_size; if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) break; if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { result = (status == TINFL_STATUS_DONE); break; } dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); } MZ_FREE(pDict); *pIn_buf_size = in_buf_ofs; return result; } #endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ // ------------------- Low-level Compression (independent from all decompression API's) #ifndef MINIZ_NO_DEFLATE_APIS // Purposely making these tables static for faster init and thread safety. static const mz_uint16 s_tdefl_len_sym[256] = { 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272, 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276, 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281, 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282, 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 }; static const mz_uint8 s_tdefl_len_extra[256] = { 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 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, 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,0 }; static const mz_uint8 s_tdefl_small_dist_sym[512] = { 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 }; static const mz_uint8 s_tdefl_small_dist_extra[512] = { 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,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,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7 }; static const mz_uint8 s_tdefl_large_dist_sym[128] = { 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 }; static const mz_uint8 s_tdefl_large_dist_extra[128] = { 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 }; // Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) { mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_ARR(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { const mz_uint32* pHist = &hist[pass << 8]; mz_uint offsets[256], cur_ofs = 0; for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } } return pCur_syms; } // tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { int root, leaf, next, avbl, used, dpth; if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } A[0].m_key += A[1].m_key; root = 0; leaf = 2; for (next=1; next < n-1; next++) { if (leaf>=n || A[root].m_key=n || (root=0; next--) A[next].m_key = A[A[next].m_key].m_key+1; avbl = 1; used = dpth = 0; root = n-2; next = n-1; while (avbl>0) { while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } avbl = 2*used; dpth++; used = 0; } } // Limits canonical Huffman code table's max code size. enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) { int i; mz_uint32 total = 0; if (code_list_len <= 1) return; for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); while (total != (1UL << max_code_size)) { pNum_codes[max_code_size]--; for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } total--; } } static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_ARR(num_codes); if (static_table) { for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; } else { tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; int num_used_syms = 0; const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); MZ_CLEAR_ARR(d->m_huff_code_sizes[table_num]); MZ_CLEAR_ARR(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); } next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); for (i = 0; i < table_len; i++) { mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; } } #define TDEFL_PUT_BITS(b, l) do { \ mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ while (d->m_bits_in >= 8) { \ if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ d->m_bit_buffer >>= 8; \ d->m_bits_in -= 8; \ } \ } MZ_MACRO_END #define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ if (rle_repeat_count < 3) { \ d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ } else { \ d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ } rle_repeat_count = 0; } } #define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ if (rle_z_count < 3) { \ d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ } else if (rle_z_count <= 10) { \ d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ } else { \ d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ } rle_z_count = 0; } } static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void tdefl_start_dynamic_block(tdefl_compressor *d) { int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; d->m_huff_count[0][256] = 1; tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); for (i = 0; i < total_code_sizes_to_pack; i++) { mz_uint8 code_size = code_sizes_to_pack[i]; if (!code_size) { TDEFL_RLE_PREV_CODE_SIZE(); if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } } else { TDEFL_RLE_ZERO_CODE_SIZE(); if (code_size != prev_code_size) { TDEFL_RLE_PREV_CODE_SIZE(); d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; } else if (++rle_repeat_count == 6) { TDEFL_RLE_PREV_CODE_SIZE(); } } prev_code_size = code_size; } if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); TDEFL_PUT_BITS(2, 2); TDEFL_PUT_BITS(num_lit_codes - 257, 5); TDEFL_PUT_BITS(num_dist_codes - 1, 5); for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) { mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); } } static void tdefl_start_static_block(tdefl_compressor *d) { mz_uint i; mz_uint8 *p = &d->m_huff_code_sizes[0][0]; for (i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; memset(d->m_huff_code_sizes[1], 5, 32); tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); TDEFL_PUT_BITS(1, 2); } static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; mz_uint8 *pOutput_buf = d->m_pOutput_buf; mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; mz_uint64 bit_buffer = d->m_bit_buffer; mz_uint bits_in = d->m_bits_in; #define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); // This sequence coaxes MSVC into using cmov's vs. jmp's. s0 = s_tdefl_small_dist_sym[match_dist & 511]; n0 = s_tdefl_small_dist_extra[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; n1 = s_tdefl_large_dist_extra[match_dist >> 8]; sym = (match_dist < 512) ? s0 : s1; num_extra_bits = (match_dist < 512) ? n0 : n1; MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } } if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; *(mz_uint64*)pOutput_buf = bit_buffer; pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; } #undef TDEFL_PUT_BITS_FAST d->m_pOutput_buf = pOutput_buf; d->m_bits_in = 0; d->m_bit_buffer = 0; while (bits_in) { mz_uint32 n = MZ_MIN(bits_in, 16); TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); bit_buffer >>= n; bits_in -= n; } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #else static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; mz_uint8 *pLZ_codes; flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); if (match_dist < 512) { sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; } else { sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; } MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { if (static_block) tdefl_start_static_block(d); else tdefl_start_dynamic_block(d); return tdefl_compress_lz_codes(d); } static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; mz_uint8 *pSaved_output_buf; mz_bool comp_block_succeeded = MZ_FALSE; int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; d->m_pOutput_buf = pOutput_buf_start; d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; MZ_ASSERT(!d->m_output_flush_remaining); d->m_output_flush_ofs = 0; d->m_output_flush_remaining = 0; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; if (!use_raw_block) comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) { mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; TDEFL_PUT_BITS(0, 2); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); } for (i = 0; i < d->m_total_lz_bytes; ++i) { TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); } } // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. else if (!comp_block_succeeded) { d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; tdefl_compress_block(d, MZ_TRUE); } if (flush) { if (flush == TDEFL_FINISH) { if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } } else { mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } } } MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { if (d->m_pPut_buf_func) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); } else if (pOutput_buf_start == d->m_output_buf) { int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); d->m_out_buf_ofs += bytes_to_copy; if ((n -= bytes_to_copy) != 0) { d->m_output_flush_ofs = bytes_to_copy; d->m_output_flush_remaining = n; } } else { d->m_out_buf_ofs += n; } } return d->m_output_flush_remaining; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for ( ; ; ) { for ( ; ; ) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32; do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); if (!probe_len) { *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; } else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); } } } #else static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const mz_uint8 *s = d->m_dict + pos, *p, *q; mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for ( ; ; ) { for ( ; ; ) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) { if (*p++ != *q++) break; } if (probe_len > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; } } } #endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN static mz_bool tdefl_compress_fast(tdefl_compressor *d) { // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); d->m_src_buf_left -= num_bytes_to_process; lookahead_size += num_bytes_to_process; while (num_bytes_to_process) { mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); memcpy(d->m_dict + dst_pos, d->m_pSrc, n); if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); d->m_pSrc += n; dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; num_bytes_to_process -= n; } dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; while (lookahead_size >= 4) { mz_uint cur_match_dist, cur_match_len = 1; mz_uint8 *pCur_dict = d->m_dict + cur_pos; mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; mz_uint probe_pos = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)lookahead_pos; if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) { const mz_uint16 *p = (const mz_uint16 *)pCur_dict; const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); mz_uint32 probe_len = 32; do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); if (!probe_len) cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) { cur_match_len = 1; *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } else { mz_uint32 s0, s1; cur_match_len = MZ_MIN(cur_match_len, lookahead_size); MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); cur_match_dist--; pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; pLZ_code_buf += 3; *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; } } else { *pLZ_code_buf++ = (mz_uint8)first_trigram; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); d->m_huff_count[0][(mz_uint8)first_trigram]++; } if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } total_lz_bytes += cur_match_len; lookahead_pos += cur_match_len; dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; MZ_ASSERT(lookahead_size >= cur_match_len); lookahead_size -= cur_match_len; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } while (lookahead_size) { mz_uint8 lit = d->m_dict[cur_pos]; total_lz_bytes++; *pLZ_code_buf++ = lit; *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } d->m_huff_count[0][lit]++; lookahead_pos++; dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; lookahead_size--; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } } d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; return MZ_TRUE; } #endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) { d->m_total_lz_bytes++; *d->m_pLZ_code_buf++ = lit; *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } d->m_huff_count[0][lit]++; } static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { mz_uint32 s0, s1; MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); d->m_total_lz_bytes += match_len; d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); match_dist -= 1; d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } static mz_bool tdefl_compress_normal(tdefl_compressor *d) { const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; tdefl_flush flush = d->m_flush; while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) { mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; } } else { while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { mz_uint8 c = *pSrc++; mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; src_buf_left--; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); } } } d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) break; // Simple lazy/greedy parsing state machine. len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; } } else { tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); } if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { cur_match_dist = cur_match_len = 0; } if (d->m_saved_match_len) { if (cur_match_len > d->m_saved_match_len) { tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); if (cur_match_len >= 128) { tdefl_record_match(d, cur_match_len, cur_match_dist); d->m_saved_match_len = 0; len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } } else { tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; } } else if (!cur_match_dist) tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) { tdefl_record_match(d, cur_match_len, cur_match_dist); len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } // Move the lookahead forward by len_to_move bytes. d->m_lookahead_pos += len_to_move; MZ_ASSERT(d->m_lookahead_size >= len_to_move); d->m_lookahead_size -= len_to_move; d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); // Check if it's time to flush the current LZ codes to the internal output buffer. if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) ) { int n; d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; } } d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; return MZ_TRUE; } static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { if (d->m_pIn_buf_size) { *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; } if (d->m_pOut_buf_size) { size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); d->m_output_flush_ofs += (mz_uint)n; d->m_output_flush_remaining -= (mz_uint)n; d->m_out_buf_ofs += n; *d->m_pOut_buf_size = d->m_out_buf_ofs; } return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; } tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) { if (!d) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return TDEFL_STATUS_BAD_PARAM; } d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; d->m_out_buf_ofs = 0; d->m_flush = flush; if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); } d->m_wants_to_finish |= (flush == TDEFL_FINISH); if ((d->m_output_flush_remaining) || (d->m_finished)) return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) { if (!tdefl_compress_fast(d)) return d->m_prev_return_status; } else #endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN { if (!tdefl_compress_normal(d)) return d->m_prev_return_status; } if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) { if (tdefl_flush_block(d, flush) < 0) return d->m_prev_return_status; d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_ARR(d->m_hash); MZ_CLEAR_ARR(d->m_next); d->m_dict_size = 0; } } return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) { MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); } tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_ARR(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); return TDEFL_STATUS_OKAY; } #ifndef MINIZ_SDL_NOUNUSED tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { return d->m_prev_return_status; } mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; } mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); MZ_FREE(pComp); return succeeded; } #endif /* MINIZ_SDL_NOUNUSED */ typedef struct { size_t m_size, m_capacity; mz_uint8 *m_pBuf; mz_bool m_expandable; } tdefl_output_buffer; static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) { tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; size_t new_size = p->m_size + len; if (new_size > p->m_capacity) { size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; } memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; return MZ_TRUE; } #ifndef MINIZ_SDL_NOUNUSED void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; out_buf.m_expandable = MZ_TRUE; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; *pOut_len = out_buf.m_size; return out_buf.m_pBuf; } size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); if (!pOut_buf) return 0; out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; return out_buf.m_size; } #endif /* MINIZ_SDL_NOUNUSED */ #ifndef MINIZ_NO_ZLIB_APIS static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; // level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) { mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; return comp_flags; } #endif //MINIZ_NO_ZLIB_APIS #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) #endif // Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at // http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. // This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. MINIZ_STATIC void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, int bpl, size_t *pLen_out, mz_uint level, mz_bool flip) { // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, y, z; mz_uint32 c; *pLen_out = 0; if (!pComp) return NULL; MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } // write dummy header for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); // compress image data tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } // write real header *pLen_out = out_buf.m_size-41; { static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; mz_uint8 pnghdr[41]={ 0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a, 0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x49,0x44,0x41,0x54}; pnghdr[18] = (mz_uint8)(w>>8); pnghdr[19] = (mz_uint8)(w>>0); pnghdr[22] = (mz_uint8)(h>>8); pnghdr[23] = (mz_uint8)(h>>0); pnghdr[25] = (chans[num_chans]); pnghdr[33] = (mz_uint8)(*pLen_out>>24); pnghdr[34] = (mz_uint8)(*pLen_out>>16); pnghdr[35] = (mz_uint8)(*pLen_out>> 8); pnghdr[36] = (mz_uint8)(*pLen_out>> 0); c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i] = (mz_uint8)(c>>24); memcpy(out_buf.m_pBuf, pnghdr, 41); } // write footer (IDAT CRC-32, followed by IEND chunk) if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24); // compute final size of file, grab compressed data buffer and return *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; } MINIZ_STATIC void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, int bpl, size_t *pLen_out) { // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, bpl, pLen_out, 6, MZ_FALSE); } #ifdef _MSC_VER #pragma warning (pop) #endif #endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ // ------------------- .ZIP archive reading #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef MINIZ_NO_STDIO #define MZ_FILE void * #else #include #include #if defined(_MSC_VER) || defined(__MINGW64__) static FILE *mz_fopen(const char *pFilename, const char *pMode) { FILE* pFile = NULL; fopen_s(&pFile, pFilename, pMode); return pFile; } static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { FILE* pFile = NULL; if (freopen_s(&pFile, pPath, pMode, pStream)) return NULL; return pFile; } #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN mz_fopen #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN mz_freopen #define MZ_DELETE_FILE remove #elif defined(__MINGW32__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__TINYC__) #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftell #define MZ_FSEEK64 fseek #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #elif defined(__GNUC__) && _LARGEFILE64_SOURCE #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen64(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello64 #define MZ_FSEEK64 fseeko64 #define MZ_FILE_STAT_STRUCT stat64 #define MZ_FILE_STAT stat64 #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) #define MZ_DELETE_FILE remove #else #ifndef MINIZ_NO_TIME #include #endif #define MZ_FILE FILE #define MZ_FOPEN(f, m) fopen(f, m) #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite #define MZ_FTELL64 ftello #define MZ_FSEEK64 fseeko #define MZ_FILE_STAT_STRUCT stat #define MZ_FILE_STAT stat #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove #endif // #ifdef _MSC_VER #endif // #ifdef MINIZ_NO_STDIO #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) // Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. enum { // ZIP archive identifiers and record sizes MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, // Central directory header record offsets MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, // Local directory header offsets MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, // End of central directory offsets MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, }; typedef struct { void *m_p; size_t m_size, m_capacity; mz_uint m_element_size; } mz_zip_array; struct mz_zip_internal_state_tag { mz_zip_array m_central_dir; mz_zip_array m_central_dir_offsets; mz_zip_array m_sorted_central_dir_offsets; MZ_FILE *m_pFile; void *m_pMem; size_t m_mem_size; size_t m_mem_capacity; }; #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) { pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); memset(pArray, 0, sizeof(mz_zip_array)); } static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) { void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) { if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) { if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } pArray->m_size = new_size; return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) { return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); } static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); return MZ_TRUE; } #ifndef MINIZ_NO_TIME static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) { struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; return mktime(&tm); } static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { #ifdef _MSC_VER struct tm tm_struct; struct tm *tm = &tm_struct; errno_t err = localtime_s(tm, &time); if (err) { *pDOS_date = 0; *pDOS_time = 0; return; } #else struct tm *tm = localtime(&time); #endif *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); } #endif #ifndef MINIZ_NO_STDIO static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { #ifdef MINIZ_NO_TIME (void)pFilename; *pDOS_date = *pDOS_time = 0; #else struct MZ_FILE_STAT_STRUCT file_stat; // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); #endif // #ifdef MINIZ_NO_TIME return MZ_TRUE; } #ifndef MINIZ_NO_TIME static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) { struct utimbuf t; t.actime = access_time; t.modtime = modified_time; return !utime(pFilename, &t); } #endif // #ifndef MINIZ_NO_TIME #endif // #ifndef MINIZ_NO_STDIO static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) { (void)flags; if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return MZ_FALSE; if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; pZip->m_zip_mode = MZ_ZIP_MODE_READING; pZip->m_archive_size = 0; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return MZ_FALSE; memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); return MZ_TRUE; } static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (l_len < r_len) : (l < r); } #define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END // Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); const int size = pZip->m_total_files; int start = (size - 2) >> 1, end; while (start >= 0) { int child, root = start; for ( ; ; ) { if ((child = (root << 1) + 1) >= size) break; child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } start--; } end = size - 1; while (end > 0) { int child, root = 0; MZ_SWAP_UINT32(pIndices[end], pIndices[0]); for ( ; ; ) { if ((child = (root << 1) + 1) >= end) break; child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) break; MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; } end--; } } static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) { mz_uint cdir_size, num_this_disk, cdir_disk_index; mz_uint64 cdir_ofs; mz_int64 cur_file_ofs; const mz_uint8 *p; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return MZ_FALSE; // Find the end of central directory record by scanning the file from the end towards the beginning. cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); for ( ; ; ) { int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) return MZ_FALSE; for (i = n - 4; i >= 0; --i) if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) break; if (i >= 0) { cur_file_ofs += i; break; } if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) return MZ_FALSE; cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); } // Read and verify the end of central directory record. if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) return MZ_FALSE; if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS))) return MZ_FALSE; num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) return MZ_FALSE; if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) return MZ_FALSE; cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) return MZ_FALSE; pZip->m_central_directory_file_ofs = cdir_ofs; if (pZip->m_total_files) { mz_uint i, n; // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices. if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) return MZ_FALSE; if (sort_central_dir) { if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) return MZ_FALSE; } if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) return MZ_FALSE; // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported). p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { mz_uint total_header_size, comp_size, decomp_size, disk_index; if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) return MZ_FALSE; MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); if (sort_central_dir) MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) return MZ_FALSE; disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); if ((disk_index != num_this_disk) && (disk_index != 1)) return MZ_FALSE; if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) return MZ_FALSE; if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) return MZ_FALSE; n -= total_header_size; p += total_header_size; } } if (sort_central_dir) mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); return MZ_TRUE; } mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) { if ((!pZip) || (!pZip->m_pRead)) return MZ_FALSE; if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_archive_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end(pZip); return MZ_FALSE; } return MZ_TRUE; } static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); return s; } mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) { if (!mz_zip_reader_init_internal(pZip, flags)) return MZ_FALSE; pZip->m_archive_size = size; pZip->m_pRead = mz_zip_mem_read_func; pZip->m_pIO_opaque = pZip; #ifdef __cplusplus pZip->m_pState->m_pMem = const_cast(pMem); #else pZip->m_pState->m_pMem = (void *)pMem; #endif pZip->m_pState->m_mem_size = size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end(pZip); return MZ_FALSE; } return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) { mz_uint64 file_size; MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); if (!pFile) return MZ_FALSE; if (MZ_FSEEK64(pFile, 0, SEEK_END)) { MZ_FCLOSE(pFile); return MZ_FALSE; } file_size = MZ_FTELL64(pFile); if (!mz_zip_reader_init_internal(pZip, flags)) { MZ_FCLOSE(pFile); return MZ_FALSE; } pZip->m_pRead = mz_zip_file_read_func; pZip->m_pIO_opaque = pZip; pZip->m_pState->m_pFile = pFile; pZip->m_archive_size = file_size; if (!mz_zip_reader_read_central_dir(pZip, flags)) { mz_zip_reader_end(pZip); return MZ_FALSE; } return MZ_TRUE; } #endif // #ifndef MINIZ_NO_STDIO mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) { return pZip ? pZip->m_total_files : 0; } static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) { if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return NULL; return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); } mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) { mz_uint m_bit_flag; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if (!p) return MZ_FALSE; m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); return (m_bit_flag & 1); } mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) { mz_uint filename_len, external_attr; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if (!p) return MZ_FALSE; // First see if the filename ends with a '/' character. filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_len) { if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') return MZ_TRUE; } // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. // FIXME: Remove this check? Is it necessary - we already check the filename. external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); if ((external_attr & 0x10) != 0) return MZ_TRUE; return MZ_FALSE; } mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) { mz_uint n; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if ((!p) || (!pStat)) return MZ_FALSE; // Unpack the central directory record. pStat->m_file_index = file_index; pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); #ifndef MINIZ_NO_TIME pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); #endif pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); // Copy as much of the filename and comment as possible. n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); pStat->m_comment_size = n; memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; return MZ_TRUE; } mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) { mz_uint n; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); if (filename_buf_size) { n = MZ_MIN(n, filename_buf_size - 1); memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pFilename[n] = '\0'; } return n + 1; } static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) { mz_uint i; if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) return 0 == memcmp(pA, pB, len); for (i = 0; i < len; ++i) if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) return MZ_FALSE; return MZ_TRUE; } static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) { const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); mz_uint8 l = 0, r = 0; pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pE = pL + MZ_MIN(l_len, r_len); while (pL < pE) { if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) break; pL++; pR++; } return (pL == pE) ? (int)(l_len - r_len) : (l - r); } static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) { mz_zip_internal_state *pState = pZip->m_pState; const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; const mz_zip_array *pCentral_dir = &pState->m_central_dir; mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); const int size = pZip->m_total_files; const mz_uint filename_len = (mz_uint)strlen(pFilename); int l = 0, h = size - 1; while (l <= h) { int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); if (!comp) return file_index; else if (comp < 0) l = m + 1; else h = m - 1; } return -1; } int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) { mz_uint file_index; size_t name_len, comment_len; if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return -1; if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) return mz_zip_reader_locate_file_binary_search(pZip, pName); name_len = strlen(pName); if (name_len > 0xFFFF) return -1; comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1; for (file_index = 0; file_index < pZip->m_total_files; file_index++) { const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; if (filename_len < name_len) continue; if (comment_len) { mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); const char *pFile_comment = pFilename + filename_len + file_extra_len; if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) continue; } if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) { int ofs = filename_len - 1; do { if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) break; } while (--ofs >= 0); ofs++; pFilename += ofs; filename_len -= ofs; } if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) return file_index; } return -1; } mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { int status = TINFL_STATUS_DONE; mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; mz_zip_archive_file_stat file_stat; void *pRead_buf; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; tinfl_decompressor inflator; if ((buf_size) && (!pBuf)) return MZ_FALSE; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) if (!file_stat.m_comp_size) return MZ_TRUE; // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). // I'm torn how to handle this case - should it fail instead? if (mz_zip_reader_is_file_a_directory(pZip, file_index)) return MZ_TRUE; // Encryption and patch files are not supported. if (file_stat.m_bit_flag & (1 | 32)) return MZ_FALSE; // This function only supports stored and deflate. if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return MZ_FALSE; // Ensure supplied output buffer is large enough. needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; if (buf_size < needed_size) return MZ_FALSE; // Read and parse the local directory entry. cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return MZ_FALSE; cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return MZ_FALSE; if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { // The file is stored or the caller has requested the compressed data. if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) return MZ_FALSE; return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); } // Decompress the file either directly from memory or from a file input buffer. tinfl_init(&inflator); if (pZip->m_pState->m_pMem) { // Read directly from the archive in memory. pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else if (pUser_read_buf) { // Use a user provided read buffer. if (!user_read_buf_size) return MZ_FALSE; pRead_buf = (mz_uint8 *)pUser_read_buf; read_buf_size = user_read_buf_size; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } else { // Temporarily allocate a read buffer. read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); #ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) #else if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) #endif return MZ_FALSE; if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return MZ_FALSE; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } do { size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; out_buf_ofs += out_buf_size; } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); if (status == TINFL_STATUS_DONE) { // Make sure the entire file was decompressed, and check its CRC. if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) status = TINFL_STATUS_FAILED; } if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); if (file_index < 0) return MZ_FALSE; return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); } mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); } mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) { return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); } void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) { mz_uint64 comp_size, uncomp_size, alloc_size; const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); void *pBuf; if (pSize) *pSize = 0; if (!p) return NULL; comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; #ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) #else if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) #endif return NULL; if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) return NULL; if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return NULL; } if (pSize) *pSize = (size_t)alloc_size; return pBuf; } void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) { int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); if (file_index < 0) { if (pSize) *pSize = 0; return MZ_FALSE; } return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); } mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; mz_zip_archive_file_stat file_stat; void *pRead_buf = NULL; void *pWrite_buf = NULL; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) if (!file_stat.m_comp_size) return MZ_TRUE; // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). // I'm torn how to handle this case - should it fail instead? if (mz_zip_reader_is_file_a_directory(pZip, file_index)) return MZ_TRUE; // Encryption and patch files are not supported. if (file_stat.m_bit_flag & (1 | 32)) return MZ_FALSE; // This function only supports stored and deflate. if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) return MZ_FALSE; // Read and parse the local directory entry. cur_file_ofs = file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return MZ_FALSE; cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) return MZ_FALSE; // Decompress the file either directly from memory or from a file input buffer. if (pZip->m_pState->m_pMem) { pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; read_buf_size = read_buf_avail = file_stat.m_comp_size; comp_remaining = 0; } else { read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return MZ_FALSE; read_buf_avail = 0; comp_remaining = file_stat.m_comp_size; } if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { // The file is stored or the caller has requested the compressed data. if (pZip->m_pState->m_pMem) { #ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) #else if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) #endif return MZ_FALSE; if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) status = TINFL_STATUS_FAILED; else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); cur_file_ofs += file_stat.m_comp_size; out_buf_ofs += file_stat.m_comp_size; comp_remaining = 0; } else { while (comp_remaining) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; out_buf_ofs += read_buf_avail; comp_remaining -= read_buf_avail; } } } else { tinfl_decompressor inflator; tinfl_init(&inflator); if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) status = TINFL_STATUS_FAILED; else { do { mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { status = TINFL_STATUS_FAILED; break; } cur_file_ofs += read_buf_avail; comp_remaining -= read_buf_avail; read_buf_ofs = 0; } in_buf_size = (size_t)read_buf_avail; status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); read_buf_avail -= in_buf_size; read_buf_ofs += in_buf_size; if (out_buf_size) { if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) { status = TINFL_STATUS_FAILED; break; } file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) { status = TINFL_STATUS_FAILED; break; } } } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); } } if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { // Make sure the entire file was decompressed, and check its CRC. if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) status = TINFL_STATUS_FAILED; } if (!pZip->m_pState->m_pMem) pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); if (pWrite_buf) pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); return status == TINFL_STATUS_DONE; } mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) { int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); if (file_index < 0) return MZ_FALSE; return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) { (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); } mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) { mz_bool status; mz_zip_archive_file_stat file_stat; MZ_FILE *pFile; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; pFile = MZ_FOPEN(pDst_filename, "wb"); if (!pFile) return MZ_FALSE; status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); if (MZ_FCLOSE(pFile) == EOF) return MZ_FALSE; #ifndef MINIZ_NO_TIME if (status) mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); #endif return status; } #endif // #ifndef MINIZ_NO_STDIO mz_bool mz_zip_reader_end(mz_zip_archive *pZip) { if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return MZ_FALSE; if (pZip->m_pState) { mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { MZ_FCLOSE(pState->m_pFile); pState->m_pFile = NULL; } #endif // #ifndef MINIZ_NO_STDIO pZip->m_pFree(pZip->m_pAlloc_opaque, pState); } pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return MZ_TRUE; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) { int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); if (file_index < 0) return MZ_FALSE; return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); } #endif // ------------------- .ZIP archive writing #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } #define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) #define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) { if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) return MZ_FALSE; if (pZip->m_file_offset_alignment) { // Ensure user specified file offset alignment is a power of 2. if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) return MZ_FALSE; } if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; if (!pZip->m_pFree) pZip->m_pFree = def_free_func; if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; pZip->m_archive_size = existing_size; pZip->m_central_directory_file_ofs = 0; pZip->m_total_files = 0; if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) return MZ_FALSE; memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); return MZ_TRUE; } static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_zip_internal_state *pState = pZip->m_pState; mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); #ifdef _MSC_VER if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) #else if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) #endif return 0; if (new_size > pState->m_mem_capacity) { void *pNew_block; size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) return 0; pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; } memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); pState->m_mem_size = (size_t)new_size; return n; } mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) { pZip->m_pWrite = mz_zip_heap_write_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) return MZ_FALSE; if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) { if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) { mz_zip_writer_end(pZip); return MZ_FALSE; } pZip->m_pState->m_mem_capacity = initial_allocation_size; } return MZ_TRUE; } #ifndef MINIZ_NO_STDIO static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) return 0; return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); } mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) { MZ_FILE *pFile; pZip->m_pWrite = mz_zip_file_write_func; pZip->m_pIO_opaque = pZip; if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) return MZ_FALSE; if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) { mz_zip_writer_end(pZip); return MZ_FALSE; } pZip->m_pState->m_pFile = pFile; if (size_to_reserve_at_beginning) { mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_ARR(buf); do { size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) { mz_zip_writer_end(pZip); return MZ_FALSE; } cur_ofs += n; size_to_reserve_at_beginning -= n; } while (size_to_reserve_at_beginning); } return MZ_TRUE; } #endif // #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) { mz_zip_internal_state *pState; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) return MZ_FALSE; // No sense in trying to write to an archive that's already at the support max size if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) return MZ_FALSE; pState = pZip->m_pState; if (pState->m_pFile) { #ifdef MINIZ_NO_STDIO pFilename; return MZ_FALSE; #else // Archive is being read from stdio - try to reopen as writable. if (pZip->m_pIO_opaque != pZip) return MZ_FALSE; if (!pFilename) return MZ_FALSE; pZip->m_pWrite = mz_zip_file_write_func; if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) { // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. mz_zip_reader_end(pZip); return MZ_FALSE; } #endif // #ifdef MINIZ_NO_STDIO } else if (pState->m_pMem) { // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. if (pZip->m_pIO_opaque != pZip) return MZ_FALSE; pState->m_mem_capacity = pState->m_mem_size; pZip->m_pWrite = mz_zip_heap_write_func; } // Archive is being read via a user provided read function - make sure the user has specified a write function too. else if (!pZip->m_pWrite) return MZ_FALSE; // Start writing new files at the archive's current central directory location. pZip->m_archive_size = pZip->m_central_directory_file_ofs; pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; pZip->m_central_directory_file_ofs = 0; return MZ_TRUE; } mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) { return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); } typedef struct { mz_zip_archive *m_pZip; mz_uint64 m_cur_archive_file_ofs; mz_uint64 m_comp_size; } mz_zip_writer_add_state; static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) { mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) return MZ_FALSE; pState->m_cur_archive_file_ofs += len; pState->m_comp_size += len; return MZ_TRUE; } static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) { (void)pZip; memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); return MZ_TRUE; } static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { (void)pZip; memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); return MZ_TRUE; } static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { mz_zip_internal_state *pState = pZip->m_pState; mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; size_t orig_central_dir_size = pState->m_central_dir.m_size; mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; // No zip64 support yet if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) return MZ_FALSE; if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) { // Try to push the central directory array back into its original state. mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return MZ_FALSE; } return MZ_TRUE; } static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) { // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. if (*pArchive_name == '/') return MZ_FALSE; while (*pArchive_name) { if ((*pArchive_name == '\\') || (*pArchive_name == ':')) return MZ_FALSE; pArchive_name++; } return MZ_TRUE; } static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) { mz_uint32 n; if (!pZip->m_file_offset_alignment) return 0; n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); } static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) { char buf[4096]; memset(buf, 0, MZ_MIN(sizeof(buf), n)); while (n) { mz_uint32 s = MZ_MIN(sizeof(buf), n); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) return MZ_FALSE; cur_file_ofs += s; n -= s; } return MZ_TRUE; } mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) { mz_uint16 method = 0, dos_time = 0, dos_date = 0; mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; tdefl_compressor *pComp = NULL; mz_bool store_data_uncompressed; mz_zip_internal_state *pState; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) return MZ_FALSE; pState = pZip->m_pState; if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) return MZ_FALSE; // No zip64 support yet if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_validate_archive_name(pArchive_name)) return MZ_FALSE; #ifndef MINIZ_NO_TIME { time_t cur_time; time(&cur_time); mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); } #endif // #ifndef MINIZ_NO_TIME archive_name_size = strlen(pArchive_name); if (archive_name_size > 0xFFFF) return MZ_FALSE; num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); // no zip64 support yet if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) return MZ_FALSE; if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) { // Set DOS Subdirectory attribute bit. ext_attributes |= 0x10; // Subdirectories cannot contain data. if ((buf_size) || (uncomp_size)) return MZ_FALSE; } // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) return MZ_FALSE; if ((!store_data_uncompressed) && (buf_size)) { if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) return MZ_FALSE; } if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } local_dir_header_ofs += num_alignment_padding_bytes; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); MZ_CLEAR_ARR(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } cur_archive_file_ofs += archive_name_size; if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); uncomp_size = buf_size; if (uncomp_size <= 3) { level = 0; store_data_uncompressed = MZ_TRUE; } } if (store_data_uncompressed) { if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } cur_archive_file_ofs += buf_size; comp_size = buf_size; if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) method = MZ_DEFLATED; } else if (buf_size) { mz_zip_writer_add_state state; state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); return MZ_FALSE; } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; method = MZ_DEFLATED; } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pComp = NULL; // no zip64 support yet if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) return MZ_FALSE; if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return MZ_FALSE; if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; MZ_FILE *pSrc_file = NULL; if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) return MZ_FALSE; if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) return MZ_FALSE; if (!mz_zip_writer_validate_archive_name(pArchive_name)) return MZ_FALSE; archive_name_size = strlen(pArchive_name); if (archive_name_size > 0xFFFF) return MZ_FALSE; num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); // no zip64 support yet if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) return MZ_FALSE; pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); if (!pSrc_file) return MZ_FALSE; MZ_FSEEK64(pSrc_file, 0, SEEK_END); uncomp_size = MZ_FTELL64(pSrc_file); MZ_FSEEK64(pSrc_file, 0, SEEK_SET); if (uncomp_size > 0xFFFFFFFF) { // No zip64 support yet MZ_FCLOSE(pSrc_file); return MZ_FALSE; } if (uncomp_size <= 3) level = 0; if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) { MZ_FCLOSE(pSrc_file); return MZ_FALSE; } local_dir_header_ofs += num_alignment_padding_bytes; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); MZ_CLEAR_ARR(local_dir_header); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { MZ_FCLOSE(pSrc_file); return MZ_FALSE; } cur_archive_file_ofs += archive_name_size; if (uncomp_size) { mz_uint64 uncomp_remaining = uncomp_size; void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); if (!pRead_buf) { MZ_FCLOSE(pSrc_file); return MZ_FALSE; } if (!level) { while (uncomp_remaining) { mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); uncomp_remaining -= n; cur_archive_file_ofs += n; } comp_size = uncomp_size; } else { mz_bool result = MZ_FALSE; mz_zip_writer_add_state state; tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); if (!pComp) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } state.m_pZip = pZip; state.m_cur_archive_file_ofs = cur_archive_file_ofs; state.m_comp_size = 0; if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) { pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } for ( ; ; ) { size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); tdefl_status status; if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) break; uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); uncomp_remaining -= in_buf_size; status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); if (status == TDEFL_STATUS_DONE) { result = MZ_TRUE; break; } else if (status != TDEFL_STATUS_OKAY) break; } pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); if (!result) { pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); MZ_FCLOSE(pSrc_file); return MZ_FALSE; } comp_size = state.m_comp_size; cur_archive_file_ofs = state.m_cur_archive_file_ofs; method = MZ_DEFLATED; } pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); } MZ_FCLOSE(pSrc_file); pSrc_file = NULL; // no zip64 support yet if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) return MZ_FALSE; if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) return MZ_FALSE; if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) return MZ_FALSE; if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) return MZ_FALSE; pZip->m_total_files++; pZip->m_archive_size = cur_archive_file_ofs; return MZ_TRUE; } #endif // #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) { mz_uint n, bit_flags, num_alignment_padding_bytes; mz_uint64 comp_bytes_remaining, local_dir_header_ofs; mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; size_t orig_central_dir_size; mz_zip_internal_state *pState; void *pBuf; const mz_uint8 *pSrc_central_header; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) return MZ_FALSE; if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) return MZ_FALSE; pState = pZip->m_pState; num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); // no zip64 support yet if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) return MZ_FALSE; cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); cur_dst_file_ofs = pZip->m_archive_size; if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return MZ_FALSE; cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) return MZ_FALSE; cur_dst_file_ofs += num_alignment_padding_bytes; local_dir_header_ofs = cur_dst_file_ofs; if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) return MZ_FALSE; cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining))))) return MZ_FALSE; while (comp_bytes_remaining) { n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } cur_src_file_ofs += n; if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } cur_dst_file_ofs += n; comp_bytes_remaining -= n; } bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); if (bit_flags & 8) { // Copy data descriptor if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); return MZ_FALSE; } cur_src_file_ofs += n; cur_dst_file_ofs += n; } pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); // no zip64 support yet if (cur_dst_file_ofs > 0xFFFFFFFF) return MZ_FALSE; orig_central_dir_size = pState->m_central_dir.m_size; memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) return MZ_FALSE; n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return MZ_FALSE; } if (pState->m_central_dir.m_size > 0xFFFFFFFF) return MZ_FALSE; n = (mz_uint32)orig_central_dir_size; if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); return MZ_FALSE; } pZip->m_total_files++; pZip->m_archive_size = cur_dst_file_ofs; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { mz_zip_internal_state *pState; mz_uint64 central_dir_ofs, central_dir_size; mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) return MZ_FALSE; pState = pZip->m_pState; // no zip64 support yet if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) return MZ_FALSE; central_dir_ofs = 0; central_dir_size = 0; if (pZip->m_total_files) { // Write central directory central_dir_ofs = pZip->m_archive_size; central_dir_size = pState->m_central_dir.m_size; pZip->m_central_directory_file_ofs = central_dir_ofs; if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) return MZ_FALSE; pZip->m_archive_size += central_dir_size; } // Write end of central directory record MZ_CLEAR_ARR(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) return MZ_FALSE; #ifndef MINIZ_NO_STDIO if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) return MZ_FALSE; #endif // #ifndef MINIZ_NO_STDIO pZip->m_archive_size += sizeof(hdr); pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; return MZ_TRUE; } mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) { if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) return MZ_FALSE; if (pZip->m_pWrite != mz_zip_heap_write_func) return MZ_FALSE; if (!mz_zip_writer_finalize_archive(pZip)) return MZ_FALSE; *pBuf = pZip->m_pState->m_pMem; *pSize = pZip->m_pState->m_mem_size; pZip->m_pState->m_pMem = NULL; pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; return MZ_TRUE; } mz_bool mz_zip_writer_end(mz_zip_archive *pZip) { mz_zip_internal_state *pState; mz_bool status = MZ_TRUE; if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) return MZ_FALSE; pState = pZip->m_pState; pZip->m_pState = NULL; mz_zip_array_clear(pZip, &pState->m_central_dir); mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); #ifndef MINIZ_NO_STDIO if (pState->m_pFile) { MZ_FCLOSE(pState->m_pFile); pState->m_pFile = NULL; } #endif // #ifndef MINIZ_NO_STDIO if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); pState->m_pMem = NULL; } pZip->m_pFree(pZip->m_pAlloc_opaque, pState); pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; return status; } #ifndef MINIZ_NO_STDIO mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { mz_bool status, created_new_archive = MZ_FALSE; mz_zip_archive zip_archive; struct MZ_FILE_STAT_STRUCT file_stat; MZ_CLEAR_OBJ(zip_archive); if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) return MZ_FALSE; if (!mz_zip_writer_validate_archive_name(pArchive_name)) return MZ_FALSE; if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { // Create a new archive. if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) return MZ_FALSE; created_new_archive = MZ_TRUE; } else { // Append to an existing archive. if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) return MZ_FALSE; if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) { mz_zip_reader_end(&zip_archive); return MZ_FALSE; } } status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) if (!mz_zip_writer_finalize_archive(&zip_archive)) status = MZ_FALSE; if (!mz_zip_writer_end(&zip_archive)) status = MZ_FALSE; if ((!status) && (created_new_archive)) { // It's a new archive and something went wrong, so just delete it. int ignoredStatus = MZ_DELETE_FILE(pZip_filename); (void)ignoredStatus; } return status; } void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) { int file_index; mz_zip_archive zip_archive; void *p = NULL; if (pSize) *pSize = 0; if ((!pZip_filename) || (!pArchive_name)) return NULL; MZ_CLEAR_OBJ(zip_archive); if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) return NULL; if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); mz_zip_reader_end(&zip_archive); return p; } #endif // #ifndef MINIZ_NO_STDIO #endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS #endif // #ifndef MINIZ_NO_ARCHIVE_APIS #ifdef __cplusplus } #endif #ifdef _MSC_VER #pragma warning (pop) #endif #endif // MINIZ_HEADER_FILE_ONLY /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to */ SDL2_image-2.8.8/src/nanosvg.h0000664000000000000000000024703214751444410012756 0ustar00/* * Copyright (c) 2013-14 Mikko Mononen memon@inside.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * * The SVG parser is based on Anti-Grain Geometry 2.4 SVG example * Copyright (C) 2002-2004 Maxim Shemanarev (McSeem) (http://www.antigrain.com/) * * Arc calculation code based on canvg (https://code.google.com/p/canvg/) * * Bounding box calculation based on http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html * */ #ifndef NANOSVG_H #define NANOSVG_H #ifndef NSVG_EXPORT #define NSVG_EXPORT #endif #ifndef NANOSVG_CPLUSPLUS #ifdef __cplusplus extern "C" { #endif #endif // NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes. // // The library suits well for anything from rendering scalable icons in your editor application to prototyping a game. // // NanoSVG supports a wide range of SVG features, but something may be missing, feel free to create a pull request! // // The shapes in the SVG images are transformed by the viewBox and converted to specified units. // That is, you should get the same looking data as your designed in your favorite app. // // NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose // to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters. // // The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. // DPI (dots-per-inch) controls how the unit conversion is done. // // If you don't know or care about the units stuff, "px" and 96 should get you going. /* Example Usage: // Load SVG NSVGimage* image; image = nsvgParseFromFile("test.svg", "px", 96); printf("size: %f x %f\n", image->width, image->height); // Use... for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { for (int i = 0; i < path->npts-1; i += 3) { float* p = &path->pts[i*2]; drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); } } } // Delete nsvgDelete(image); */ enum NSVGpaintType { NSVG_PAINT_UNDEF = -1, NSVG_PAINT_NONE = 0, NSVG_PAINT_COLOR = 1, NSVG_PAINT_LINEAR_GRADIENT = 2, NSVG_PAINT_RADIAL_GRADIENT = 3 }; enum NSVGspreadType { NSVG_SPREAD_PAD = 0, NSVG_SPREAD_REFLECT = 1, NSVG_SPREAD_REPEAT = 2 }; enum NSVGlineJoin { NSVG_JOIN_MITER = 0, NSVG_JOIN_ROUND = 1, NSVG_JOIN_BEVEL = 2 }; enum NSVGlineCap { NSVG_CAP_BUTT = 0, NSVG_CAP_ROUND = 1, NSVG_CAP_SQUARE = 2 }; enum NSVGfillRule { NSVG_FILLRULE_NONZERO = 0, NSVG_FILLRULE_EVENODD = 1 }; enum NSVGflags { NSVG_FLAGS_VISIBLE = 0x01 }; typedef struct NSVGgradientStop { unsigned int color; float offset; } NSVGgradientStop; typedef struct NSVGgradient { float xform[6]; char spread; float fx, fy; int nstops; NSVGgradientStop stops[1]; } NSVGgradient; typedef struct NSVGpaint { signed char type; union { unsigned int color; NSVGgradient* gradient; }; } NSVGpaint; typedef struct NSVGpath { float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... int npts; // Total number of bezier points. char closed; // Flag indicating if shapes should be treated as closed. float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. struct NSVGpath* next; // Pointer to next path, or NULL if last element. } NSVGpath; typedef struct NSVGshape { char id[64]; // Optional 'id' attr of the shape or its group NSVGpaint fill; // Fill paint NSVGpaint stroke; // Stroke paint float opacity; // Opacity of the shape. float strokeWidth; // Stroke width (scaled). float strokeDashOffset; // Stroke dash offset (scaled). float strokeDashArray[8]; // Stroke dash array (scaled). char strokeDashCount; // Number of dash values in dash array. char strokeLineJoin; // Stroke join type. char strokeLineCap; // Stroke cap type. float miterLimit; // Miter limit char fillRule; // Fill rule, see NSVGfillRule. unsigned char flags; // Logical or of NSVG_FLAGS_* flags float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. char fillGradient[64]; // Optional 'id' of fill gradient char strokeGradient[64]; // Optional 'id' of stroke gradient float xform[6]; // Root transformation for fill/stroke gradient NSVGpath* paths; // Linked list of paths in the image. struct NSVGshape* next; // Pointer to next shape, or NULL if last element. } NSVGshape; typedef struct NSVGimage { float width; // Width of the image. float height; // Height of the image. NSVGshape* shapes; // Linked list of shapes in the image. } NSVGimage; #ifdef HAVE_STDIO_H // Parses SVG file from a file, returns SVG image as paths. NSVG_EXPORT NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi); #endif // Parses SVG file from a null terminated string, returns SVG image as paths. // Important note: changes the string. NSVG_EXPORT NSVGimage* nsvgParse(char* input, const char* units, float dpi); #if 0 // Duplicates a path. NSVG_EXPORT NSVGpath* nsvgDuplicatePath(NSVGpath* p); #endif // Deletes an image. NSVG_EXPORT void nsvgDelete(NSVGimage* image); #ifndef NANOSVG_CPLUSPLUS #ifdef __cplusplus } #endif #endif #ifdef NANOSVG_IMPLEMENTATION /* #include #include #include */ #define NSVG_PI (3.14159265358979323846264338327f) #define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. #define NSVG_ALIGN_MIN 0 #define NSVG_ALIGN_MID 1 #define NSVG_ALIGN_MAX 2 #define NSVG_ALIGN_NONE 0 #define NSVG_ALIGN_MEET 1 #define NSVG_ALIGN_SLICE 2 #define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0) #define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) #ifdef _MSC_VER #pragma warning (disable: 4996) // Switch off security warnings #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings #ifdef __cplusplus #define NSVG_INLINE inline #else #define NSVG_INLINE #endif #else #define NSVG_INLINE inline #endif static int nsvg__isspace(char c) { return strchr(" \t\n\v\f\r", c) != 0; } static int nsvg__isdigit(char c) { return c >= '0' && c <= '9'; } static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; } static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; } // Simple XML parser #define NSVG_XML_TAG 1 #define NSVG_XML_CONTENT 2 #define NSVG_XML_MAX_ATTRIBS 256 static void nsvg__parseContent(char* s, void (*contentCb)(void* ud, const char* s), void* ud) { // Trim start white spaces while (*s && nsvg__isspace(*s)) s++; if (!*s) return; if (contentCb) (*contentCb)(ud, s); } static void nsvg__parseElement(char* s, void (*startelCb)(void* ud, const char* el, const char** attr), void (*endelCb)(void* ud, const char* el), void* ud) { const char* attr[NSVG_XML_MAX_ATTRIBS]; int nattr = 0; char* name; int start = 0; int end = 0; char quote; // Skip white space after the '<' while (*s && nsvg__isspace(*s)) s++; // Check if the tag is end tag if (*s == '/') { s++; end = 1; } else { start = 1; } // Skip comments, data and preprocessor stuff. if (!*s || *s == '?' || *s == '!') return; // Get tag name name = s; while (*s && !nsvg__isspace(*s)) s++; if (*s) { *s++ = '\0'; } // Get attribs while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) { char* attr_name = NULL; char* attr_value = NULL; // Skip white space before the attrib name while (*s && nsvg__isspace(*s)) s++; if (!*s) break; if (*s == '/') { end = 1; break; } attr_name = s; // Find end of the attrib name. while (*s && !nsvg__isspace(*s) && *s != '=') s++; if (*s) { *s++ = '\0'; } // Skip until the beginning of the value. while (*s && *s != '\"' && *s != '\'') s++; if (!*s) break; quote = *s; s++; // Store value and find the end of it. attr_value = s; while (*s && *s != quote) s++; if (*s) { *s++ = '\0'; } // Store only well formed attributes if (attr_name && attr_value) { attr[nattr++] = attr_name; attr[nattr++] = attr_value; } } // List terminator attr[nattr++] = 0; attr[nattr++] = 0; // Call callbacks. if (start && startelCb) (*startelCb)(ud, name, attr); if (end && endelCb) (*endelCb)(ud, name); } static int nsvg__parseXML(char* input, void (*startelCb)(void* ud, const char* el, const char** attr), void (*endelCb)(void* ud, const char* el), void (*contentCb)(void* ud, const char* s), void* ud) { char* s = input; char* mark = s; int state = NSVG_XML_CONTENT; while (*s) { if (*s == '<' && state == NSVG_XML_CONTENT) { // Start of a tag *s++ = '\0'; nsvg__parseContent(mark, contentCb, ud); mark = s; state = NSVG_XML_TAG; } else if (*s == '>' && state == NSVG_XML_TAG) { // Start of a content or new tag. *s++ = '\0'; nsvg__parseElement(mark, startelCb, endelCb, ud); mark = s; state = NSVG_XML_CONTENT; } else { s++; } } return 1; } /* Simple SVG parser. */ #define NSVG_MAX_ATTR 128 enum NSVGgradientUnits { NSVG_USER_SPACE = 0, NSVG_OBJECT_SPACE = 1 }; #define NSVG_MAX_DASHES 8 enum NSVGunits { NSVG_UNITS_USER, NSVG_UNITS_PX, NSVG_UNITS_PT, NSVG_UNITS_PC, NSVG_UNITS_MM, NSVG_UNITS_CM, NSVG_UNITS_IN, NSVG_UNITS_PERCENT, NSVG_UNITS_EM, NSVG_UNITS_EX }; typedef struct NSVGcoordinate { float value; int units; } NSVGcoordinate; typedef struct NSVGlinearData { NSVGcoordinate x1, y1, x2, y2; } NSVGlinearData; typedef struct NSVGradialData { NSVGcoordinate cx, cy, r, fx, fy; } NSVGradialData; typedef struct NSVGgradientData { char id[64]; char ref[64]; signed char type; union { NSVGlinearData linear; NSVGradialData radial; }; char spread; char units; float xform[6]; int nstops; NSVGgradientStop* stops; struct NSVGgradientData* next; } NSVGgradientData; typedef struct NSVGattrib { char id[64]; float xform[6]; unsigned int fillColor; unsigned int strokeColor; float opacity; float fillOpacity; float strokeOpacity; char fillGradient[64]; char strokeGradient[64]; float strokeWidth; float strokeDashOffset; float strokeDashArray[NSVG_MAX_DASHES]; int strokeDashCount; char strokeLineJoin; char strokeLineCap; float miterLimit; char fillRule; float fontSize; unsigned int stopColor; float stopOpacity; float stopOffset; char hasFill; char hasStroke; char visible; } NSVGattrib; typedef struct NSVGstyles { char* name; char* description; struct NSVGstyles* next; } NSVGstyles; typedef struct NSVGparser { NSVGattrib attr[NSVG_MAX_ATTR]; int attrHead; float* pts; int npts; int cpts; NSVGpath* plist; NSVGimage* image; NSVGstyles* styles; NSVGgradientData* gradients; NSVGshape* shapesTail; float viewMinx, viewMiny, viewWidth, viewHeight; int alignX, alignY, alignType; float dpi; char pathFlag; char defsFlag; char styleFlag; } NSVGparser; static void nsvg__xformIdentity(float* t) { t[0] = 1.0f; t[1] = 0.0f; t[2] = 0.0f; t[3] = 1.0f; t[4] = 0.0f; t[5] = 0.0f; } static void nsvg__xformSetTranslation(float* t, float tx, float ty) { t[0] = 1.0f; t[1] = 0.0f; t[2] = 0.0f; t[3] = 1.0f; t[4] = tx; t[5] = ty; } static void nsvg__xformSetScale(float* t, float sx, float sy) { t[0] = sx; t[1] = 0.0f; t[2] = 0.0f; t[3] = sy; t[4] = 0.0f; t[5] = 0.0f; } static void nsvg__xformSetSkewX(float* t, float a) { t[0] = 1.0f; t[1] = 0.0f; t[2] = tanf(a); t[3] = 1.0f; t[4] = 0.0f; t[5] = 0.0f; } static void nsvg__xformSetSkewY(float* t, float a) { t[0] = 1.0f; t[1] = tanf(a); t[2] = 0.0f; t[3] = 1.0f; t[4] = 0.0f; t[5] = 0.0f; } static void nsvg__xformSetRotation(float* t, float a) { float cs = cosf(a), sn = sinf(a); t[0] = cs; t[1] = sn; t[2] = -sn; t[3] = cs; t[4] = 0.0f; t[5] = 0.0f; } static void nsvg__xformMultiply(float* t, float* s) { float t0 = t[0] * s[0] + t[1] * s[2]; float t2 = t[2] * s[0] + t[3] * s[2]; float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; t[1] = t[0] * s[1] + t[1] * s[3]; t[3] = t[2] * s[1] + t[3] * s[3]; t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; t[0] = t0; t[2] = t2; t[4] = t4; } static void nsvg__xformInverse(float* inv, float* t) { double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; if (det > -1e-6 && det < 1e-6) { nsvg__xformIdentity(t); return; } invdet = 1.0 / det; inv[0] = (float)(t[3] * invdet); inv[2] = (float)(-t[2] * invdet); inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); inv[1] = (float)(-t[1] * invdet); inv[3] = (float)(t[0] * invdet); inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); } static void nsvg__xformPremultiply(float* t, float* s) { float s2[6]; memcpy(s2, s, sizeof(float)*6); nsvg__xformMultiply(s2, t); memcpy(t, s2, sizeof(float)*6); } static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t) { *dx = x*t[0] + y*t[2] + t[4]; *dy = x*t[1] + y*t[3] + t[5]; } static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t) { *dx = x*t[0] + y*t[2]; *dy = x*t[1] + y*t[3]; } #define NSVG_EPSILON (1e-12) static int nsvg__ptInBounds(float* pt, float* bounds) { return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; } static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) { double it = 1.0-t; return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3; } static void nsvg__curveBounds(float* bounds, float* curve) { int i, j, count; double roots[2], a, b, c, b2ac, t, v; float* v0 = &curve[0]; float* v1 = &curve[2]; float* v2 = &curve[4]; float* v3 = &curve[6]; // Start the bounding box by end points bounds[0] = nsvg__minf(v0[0], v3[0]); bounds[1] = nsvg__minf(v0[1], v3[1]); bounds[2] = nsvg__maxf(v0[0], v3[0]); bounds[3] = nsvg__maxf(v0[1], v3[1]); // Bezier curve fits inside the convex hull of it's control points. // If control points are inside the bounds, we're done. if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) return; // Add bezier curve inflection points in X and Y. for (i = 0; i < 2; i++) { a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; c = 3.0 * v1[i] - 3.0 * v0[i]; count = 0; if (fabs(a) < NSVG_EPSILON) { if (fabs(b) > NSVG_EPSILON) { t = -c / b; if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) roots[count++] = t; } } else { b2ac = b*b - 4.0*c*a; if (b2ac > NSVG_EPSILON) { t = (-b + sqrt(b2ac)) / (2.0 * a); if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) roots[count++] = t; t = (-b - sqrt(b2ac)) / (2.0 * a); if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) roots[count++] = t; } } for (j = 0; j < count; j++) { v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); bounds[0+i] = nsvg__minf(bounds[0+i], (float)v); bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v); } } } static NSVGparser* nsvg__createParser(void) { NSVGparser* p; p = (NSVGparser*)malloc(sizeof(NSVGparser)); if (p == NULL) goto error; memset(p, 0, sizeof(NSVGparser)); p->image = (NSVGimage*)malloc(sizeof(NSVGimage)); if (p->image == NULL) goto error; memset(p->image, 0, sizeof(NSVGimage)); // Init style nsvg__xformIdentity(p->attr[0].xform); memset(p->attr[0].id, 0, sizeof p->attr[0].id); p->attr[0].fillColor = NSVG_RGB(0,0,0); p->attr[0].strokeColor = NSVG_RGB(0,0,0); p->attr[0].opacity = 1; p->attr[0].fillOpacity = 1; p->attr[0].strokeOpacity = 1; p->attr[0].stopOpacity = 1; p->attr[0].strokeWidth = 1; p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; p->attr[0].strokeLineCap = NSVG_CAP_BUTT; p->attr[0].miterLimit = 4; p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; p->attr[0].hasFill = 1; p->attr[0].visible = 1; return p; error: if (p) { if (p->image) free(p->image); free(p); } return NULL; } static void nsvg__deleteStyles(NSVGstyles* style) { while (style) { NSVGstyles *next = style->next; if (style->name) free(style->name); if (style->description) free(style->description); free(style); style = next; } } static void nsvg__deletePaths(NSVGpath* path) { while (path) { NSVGpath *next = path->next; if (path->pts != NULL) free(path->pts); free(path); path = next; } } static void nsvg__deletePaint(NSVGpaint* paint) { if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) free(paint->gradient); } static void nsvg__deleteGradientData(NSVGgradientData* grad) { NSVGgradientData* next; while (grad != NULL) { next = grad->next; free(grad->stops); free(grad); grad = next; } } static void nsvg__deleteParser(NSVGparser* p) { if (p != NULL) { nsvg__deleteStyles(p->styles); nsvg__deletePaths(p->plist); nsvg__deleteGradientData(p->gradients); nsvgDelete(p->image); free(p->pts); free(p); } } static void nsvg__resetPath(NSVGparser* p) { p->npts = 0; } static void nsvg__addPoint(NSVGparser* p, float x, float y) { if (p->npts+1 > p->cpts) { p->cpts = p->cpts ? p->cpts*2 : 8; p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float)); if (!p->pts) return; } p->pts[p->npts*2+0] = x; p->pts[p->npts*2+1] = y; p->npts++; } static void nsvg__moveTo(NSVGparser* p, float x, float y) { if (p->npts > 0) { p->pts[(p->npts-1)*2+0] = x; p->pts[(p->npts-1)*2+1] = y; } else { nsvg__addPoint(p, x, y); } } static void nsvg__lineTo(NSVGparser* p, float x, float y) { float px,py, dx,dy; if (p->npts > 0) { px = p->pts[(p->npts-1)*2+0]; py = p->pts[(p->npts-1)*2+1]; dx = x - px; dy = y - py; nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f); nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f); nsvg__addPoint(p, x, y); } } static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) { if (p->npts > 0) { nsvg__addPoint(p, cpx1, cpy1); nsvg__addPoint(p, cpx2, cpy2); nsvg__addPoint(p, x, y); } } static NSVGattrib* nsvg__getAttr(NSVGparser* p) { return &p->attr[p->attrHead]; } static void nsvg__pushAttr(NSVGparser* p) { if (p->attrHead < NSVG_MAX_ATTR-1) { p->attrHead++; memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib)); } } static void nsvg__popAttr(NSVGparser* p) { if (p->attrHead > 0) p->attrHead--; } static float nsvg__actualOrigX(NSVGparser* p) { return p->viewMinx; } static float nsvg__actualOrigY(NSVGparser* p) { return p->viewMiny; } static float nsvg__actualWidth(NSVGparser* p) { return p->viewWidth; } static float nsvg__actualHeight(NSVGparser* p) { return p->viewHeight; } static float nsvg__actualLength(NSVGparser* p) { float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); return sqrtf(w*w + h*h) / sqrtf(2.0f); } static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length) { NSVGattrib* attr = nsvg__getAttr(p); switch (c.units) { case NSVG_UNITS_USER: return c.value; case NSVG_UNITS_PX: return c.value; case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi; case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi; case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi; case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi; case NSVG_UNITS_IN: return c.value * p->dpi; case NSVG_UNITS_EM: return c.value * attr->fontSize; case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length; default: return c.value; } /*return c.value; UNREACHABLE CODE */ } static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id) { NSVGgradientData* grad = p->gradients; if (id == NULL || *id == '\0') return NULL; while (grad != NULL) { if (strcmp(grad->id, id) == 0) return grad; grad = grad->next; } return NULL; } static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, float *xform, signed char* paintType) { NSVGgradientData* data = NULL; NSVGgradientData* ref = NULL; NSVGgradientStop* stops = NULL; NSVGgradient* grad; float ox, oy, sw, sh, sl; int nstops = 0; int refIter; data = nsvg__findGradientData(p, id); if (data == NULL) return NULL; // TODO: use ref to fill in all unset values too. ref = data; refIter = 0; while (ref != NULL) { NSVGgradientData* nextRef = NULL; if (stops == NULL && ref->stops != NULL) { stops = ref->stops; nstops = ref->nstops; break; } nextRef = nsvg__findGradientData(p, ref->ref); if (nextRef == ref) break; // prevent infite loops on malformed data ref = nextRef; refIter++; if (refIter > 32) break; // prevent infite loops on malformed data } if (stops == NULL) return NULL; grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1)); if (grad == NULL) return NULL; // The shape width and height. if (data->units == NSVG_OBJECT_SPACE) { ox = localBounds[0]; oy = localBounds[1]; sw = localBounds[2] - localBounds[0]; sh = localBounds[3] - localBounds[1]; } else { ox = nsvg__actualOrigX(p); oy = nsvg__actualOrigY(p); sw = nsvg__actualWidth(p); sh = nsvg__actualHeight(p); } sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f); if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { float x1, y1, x2, y2, dx, dy; x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); // Calculate transform aligned to the line dx = x2 - x1; dy = y2 - y1; grad->xform[0] = dy; grad->xform[1] = -dx; grad->xform[2] = dx; grad->xform[3] = dy; grad->xform[4] = x1; grad->xform[5] = y1; } else { float cx, cy, fx, fy, r; cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); r = nsvg__convertToPixels(p, data->radial.r, 0, sl); // Calculate transform aligned to the circle grad->xform[0] = r; grad->xform[1] = 0; grad->xform[2] = 0; grad->xform[3] = r; grad->xform[4] = cx; grad->xform[5] = cy; grad->fx = fx / r; grad->fy = fy / r; } nsvg__xformMultiply(grad->xform, data->xform); nsvg__xformMultiply(grad->xform, xform); grad->spread = data->spread; memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop)); grad->nstops = nstops; *paintType = data->type; return grad; } static float nsvg__getAverageScale(float* t) { float sx = sqrtf(t[0]*t[0] + t[2]*t[2]); float sy = sqrtf(t[1]*t[1] + t[3]*t[3]); return (sx + sy) * 0.5f; } static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform) { NSVGpath* path; float curve[4*2], curveBounds[4]; int i, first = 1; for (path = shape->paths; path != NULL; path = path->next) { nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); for (i = 0; i < path->npts-1; i += 3) { nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform); nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform); nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform); nsvg__curveBounds(curveBounds, curve); if (first) { bounds[0] = curveBounds[0]; bounds[1] = curveBounds[1]; bounds[2] = curveBounds[2]; bounds[3] = curveBounds[3]; first = 0; } else { bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); } curve[0] = curve[6]; curve[1] = curve[7]; } } } static void nsvg__addShape(NSVGparser* p) { NSVGattrib* attr = nsvg__getAttr(p); float scale = 1.0f; NSVGshape* shape; NSVGpath* path; int i; if (p->plist == NULL) return; shape = (NSVGshape*)malloc(sizeof(NSVGshape)); if (shape == NULL) goto error; memset(shape, 0, sizeof(NSVGshape)); memcpy(shape->id, attr->id, sizeof shape->id); memcpy(shape->fillGradient, attr->fillGradient, sizeof shape->fillGradient); memcpy(shape->strokeGradient, attr->strokeGradient, sizeof shape->strokeGradient); memcpy(shape->xform, attr->xform, sizeof shape->xform); scale = nsvg__getAverageScale(attr->xform); shape->strokeWidth = attr->strokeWidth * scale; shape->strokeDashOffset = attr->strokeDashOffset * scale; shape->strokeDashCount = (char)attr->strokeDashCount; for (i = 0; i < attr->strokeDashCount; i++) shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; shape->strokeLineJoin = attr->strokeLineJoin; shape->strokeLineCap = attr->strokeLineCap; shape->miterLimit = attr->miterLimit; shape->fillRule = attr->fillRule; shape->opacity = attr->opacity; shape->paths = p->plist; p->plist = NULL; // Calculate shape bounds shape->bounds[0] = shape->paths->bounds[0]; shape->bounds[1] = shape->paths->bounds[1]; shape->bounds[2] = shape->paths->bounds[2]; shape->bounds[3] = shape->paths->bounds[3]; for (path = shape->paths->next; path != NULL; path = path->next) { shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]); shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]); shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]); shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]); } // Set fill if (attr->hasFill == 0) { shape->fill.type = NSVG_PAINT_NONE; } else if (attr->hasFill == 1) { shape->fill.type = NSVG_PAINT_COLOR; shape->fill.color = attr->fillColor; shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24; } else if (attr->hasFill == 2) { shape->fill.type = NSVG_PAINT_UNDEF; } // Set stroke if (attr->hasStroke == 0) { shape->stroke.type = NSVG_PAINT_NONE; } else if (attr->hasStroke == 1) { shape->stroke.type = NSVG_PAINT_COLOR; shape->stroke.color = attr->strokeColor; shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24; } else if (attr->hasStroke == 2) { shape->stroke.type = NSVG_PAINT_UNDEF; } // Set flags shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); // Add to tail if (p->image->shapes == NULL) p->image->shapes = shape; else p->shapesTail->next = shape; p->shapesTail = shape; return; error: if (shape) free(shape); } static void nsvg__addPath(NSVGparser* p, char closed) { NSVGattrib* attr = nsvg__getAttr(p); NSVGpath* path = NULL; float bounds[4]; float* curve; int i; if (p->npts < 4) return; if (closed) nsvg__lineTo(p, p->pts[0], p->pts[1]); // Expect 1 + N*3 points (N = number of cubic bezier segments). if ((p->npts % 3) != 1) return; path = (NSVGpath*)malloc(sizeof(NSVGpath)); if (path == NULL) goto error; memset(path, 0, sizeof(NSVGpath)); path->pts = (float*)malloc(p->npts*2*sizeof(float)); if (path->pts == NULL) goto error; path->closed = closed; path->npts = p->npts; // Transform path. for (i = 0; i < p->npts; ++i) nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform); // Find bounds for (i = 0; i < path->npts-1; i += 3) { curve = &path->pts[i*2]; nsvg__curveBounds(bounds, curve); if (i == 0) { path->bounds[0] = bounds[0]; path->bounds[1] = bounds[1]; path->bounds[2] = bounds[2]; path->bounds[3] = bounds[3]; } else { path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); } } path->next = p->plist; p->plist = path; return; error: if (path != NULL) { if (path->pts != NULL) free(path->pts); free(path); } } // We roll our own string to float because the std library one uses locale and messes things up. static float nsvg__atof(const char* s) { char* cur = (char*)s; char* end = NULL; double res = 0.0, sign = 1.0; long long intPart = 0, fracPart = 0; char hasIntPart = 0, hasFracPart = 0; // Parse optional sign if (*cur == '+') { cur++; } else if (*cur == '-') { sign = -1; cur++; } // Parse integer part if (nsvg__isdigit(*cur)) { // Parse digit sequence intPart = strtoll(cur, &end, 10); if (cur != end) { res = (double)intPart; hasIntPart = 1; cur = end; } } // Parse fractional part. if (*cur == '.') { cur++; // Skip '.' if (nsvg__isdigit(*cur)) { // Parse digit sequence fracPart = strtoll(cur, &end, 10); if (cur != end) { res += (double)fracPart / pow(10.0, (double)(end - cur)); hasFracPart = 1; cur = end; } } } // A valid number should have integer or fractional part. if (!hasIntPart && !hasFracPart) return 0.0f; // Parse optional exponent if (*cur == 'e' || *cur == 'E') { long expPart = 0; cur++; // skip 'E' expPart = strtol(cur, &end, 10); // Parse digit sequence with sign if (cur != end) { res *= pow(10.0, (double)expPart); } } return (float)(res * sign); } static const char* nsvg__parseNumber(const char* s, char* it, const int size) { const int last = size-1; int i = 0; // sign if (*s == '-' || *s == '+') { if (i < last) it[i++] = *s; s++; } // integer part while (*s && nsvg__isdigit(*s)) { if (i < last) it[i++] = *s; s++; } if (*s == '.') { // decimal point if (i < last) it[i++] = *s; s++; // fraction part while (*s && nsvg__isdigit(*s)) { if (i < last) it[i++] = *s; s++; } } // exponent if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) { if (i < last) it[i++] = *s; s++; if (*s == '-' || *s == '+') { if (i < last) it[i++] = *s; s++; } while (*s && nsvg__isdigit(*s)) { if (i < last) it[i++] = *s; s++; } } it[i] = '\0'; return s; } static const char* nsvg__getNextPathItemWhenArcFlag(const char* s, char* it) { it[0] = '\0'; while (*s && (nsvg__isspace(*s) || *s == ',')) s++; if (!*s) return s; if (*s == '0' || *s == '1') { it[0] = *s++; it[1] = '\0'; return s; } return s; } static const char* nsvg__getNextPathItem(const char* s, char* it) { it[0] = '\0'; // Skip white spaces and commas while (*s && (nsvg__isspace(*s) || *s == ',')) s++; if (!*s) return s; if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { s = nsvg__parseNumber(s, it, 64); } else { // Parse command it[0] = *s++; it[1] = '\0'; return s; } return s; } static unsigned int nsvg__parseColorHex(const char* str) { unsigned int r=0, g=0, b=0; if (sscanf(str, "#%2x%2x%2x", &r, &g, &b) == 3 ) // 2 digit hex return NSVG_RGB(r, g, b); if (sscanf(str, "#%1x%1x%1x", &r, &g, &b) == 3 ) // 1 digit hex, e.g. #abc -> 0xccbbaa return NSVG_RGB(r*17, g*17, b*17); // same effect as (r<<4|r), (g<<4|g), .. return NSVG_RGB(128, 128, 128); } // Parse rgb color. The pointer 'str' must point at "rgb(" (4+ characters). // This function returns gray (rgb(128, 128, 128) == '#808080') on parse errors // for backwards compatibility. Note: other image viewers return black instead. static unsigned int nsvg__parseColorRGB(const char* str) { int i; unsigned int rgbi[3]; float rgbf[3]; // try decimal integers first if (sscanf(str, "rgb(%u, %u, %u)", &rgbi[0], &rgbi[1], &rgbi[2]) != 3) { // integers failed, try percent values (float, locale independent) const char delimiter[3] = {',', ',', ')'}; str += 4; // skip "rgb(" for (i = 0; i < 3; i++) { while (*str && (nsvg__isspace(*str))) str++; // skip leading spaces if (*str == '+') str++; // skip '+' (don't allow '-') if (!*str) break; rgbf[i] = nsvg__atof(str); // Note 1: it would be great if nsvg__atof() returned how many // bytes it consumed but it doesn't. We need to skip the number, // the '%' character, spaces, and the delimiter ',' or ')'. // Note 2: The following code does not allow values like "33.%", // i.e. a decimal point w/o fractional part, but this is consistent // with other image viewers, e.g. firefox, chrome, eog, gimp. while (*str && nsvg__isdigit(*str)) str++; // skip integer part if (*str == '.') { str++; if (!nsvg__isdigit(*str)) break; // error: no digit after '.' while (*str && nsvg__isdigit(*str)) str++; // skip fractional part } if (*str == '%') str++; else break; while (*str && nsvg__isspace(*str)) str++; if (*str == delimiter[i]) str++; else break; } if (i == 3) { rgbi[0] = roundf(rgbf[0] * 2.55f); rgbi[1] = roundf(rgbf[1] * 2.55f); rgbi[2] = roundf(rgbf[2] * 2.55f); } else { rgbi[0] = rgbi[1] = rgbi[2] = 128; } } // clip values as the CSS spec requires for (i = 0; i < 3; i++) { if (rgbi[i] > 255) rgbi[i] = 255; } return NSVG_RGB(rgbi[0], rgbi[1], rgbi[2]); } typedef struct NSVGNamedColor { const char* name; unsigned int color; } NSVGNamedColor; static NSVGNamedColor nsvg__colors[] = { { "red", NSVG_RGB(255, 0, 0) }, { "green", NSVG_RGB( 0, 128, 0) }, { "blue", NSVG_RGB( 0, 0, 255) }, { "yellow", NSVG_RGB(255, 255, 0) }, { "cyan", NSVG_RGB( 0, 255, 255) }, { "magenta", NSVG_RGB(255, 0, 255) }, { "black", NSVG_RGB( 0, 0, 0) }, { "grey", NSVG_RGB(128, 128, 128) }, { "gray", NSVG_RGB(128, 128, 128) }, { "white", NSVG_RGB(255, 255, 255) }, #ifdef NANOSVG_ALL_COLOR_KEYWORDS { "aliceblue", NSVG_RGB(240, 248, 255) }, { "antiquewhite", NSVG_RGB(250, 235, 215) }, { "aqua", NSVG_RGB( 0, 255, 255) }, { "aquamarine", NSVG_RGB(127, 255, 212) }, { "azure", NSVG_RGB(240, 255, 255) }, { "beige", NSVG_RGB(245, 245, 220) }, { "bisque", NSVG_RGB(255, 228, 196) }, { "blanchedalmond", NSVG_RGB(255, 235, 205) }, { "blueviolet", NSVG_RGB(138, 43, 226) }, { "brown", NSVG_RGB(165, 42, 42) }, { "burlywood", NSVG_RGB(222, 184, 135) }, { "cadetblue", NSVG_RGB( 95, 158, 160) }, { "chartreuse", NSVG_RGB(127, 255, 0) }, { "chocolate", NSVG_RGB(210, 105, 30) }, { "coral", NSVG_RGB(255, 127, 80) }, { "cornflowerblue", NSVG_RGB(100, 149, 237) }, { "cornsilk", NSVG_RGB(255, 248, 220) }, { "crimson", NSVG_RGB(220, 20, 60) }, { "darkblue", NSVG_RGB( 0, 0, 139) }, { "darkcyan", NSVG_RGB( 0, 139, 139) }, { "darkgoldenrod", NSVG_RGB(184, 134, 11) }, { "darkgray", NSVG_RGB(169, 169, 169) }, { "darkgreen", NSVG_RGB( 0, 100, 0) }, { "darkgrey", NSVG_RGB(169, 169, 169) }, { "darkkhaki", NSVG_RGB(189, 183, 107) }, { "darkmagenta", NSVG_RGB(139, 0, 139) }, { "darkolivegreen", NSVG_RGB( 85, 107, 47) }, { "darkorange", NSVG_RGB(255, 140, 0) }, { "darkorchid", NSVG_RGB(153, 50, 204) }, { "darkred", NSVG_RGB(139, 0, 0) }, { "darksalmon", NSVG_RGB(233, 150, 122) }, { "darkseagreen", NSVG_RGB(143, 188, 143) }, { "darkslateblue", NSVG_RGB( 72, 61, 139) }, { "darkslategray", NSVG_RGB( 47, 79, 79) }, { "darkslategrey", NSVG_RGB( 47, 79, 79) }, { "darkturquoise", NSVG_RGB( 0, 206, 209) }, { "darkviolet", NSVG_RGB(148, 0, 211) }, { "deeppink", NSVG_RGB(255, 20, 147) }, { "deepskyblue", NSVG_RGB( 0, 191, 255) }, { "dimgray", NSVG_RGB(105, 105, 105) }, { "dimgrey", NSVG_RGB(105, 105, 105) }, { "dodgerblue", NSVG_RGB( 30, 144, 255) }, { "firebrick", NSVG_RGB(178, 34, 34) }, { "floralwhite", NSVG_RGB(255, 250, 240) }, { "forestgreen", NSVG_RGB( 34, 139, 34) }, { "fuchsia", NSVG_RGB(255, 0, 255) }, { "gainsboro", NSVG_RGB(220, 220, 220) }, { "ghostwhite", NSVG_RGB(248, 248, 255) }, { "gold", NSVG_RGB(255, 215, 0) }, { "goldenrod", NSVG_RGB(218, 165, 32) }, { "greenyellow", NSVG_RGB(173, 255, 47) }, { "honeydew", NSVG_RGB(240, 255, 240) }, { "hotpink", NSVG_RGB(255, 105, 180) }, { "indianred", NSVG_RGB(205, 92, 92) }, { "indigo", NSVG_RGB( 75, 0, 130) }, { "ivory", NSVG_RGB(255, 255, 240) }, { "khaki", NSVG_RGB(240, 230, 140) }, { "lavender", NSVG_RGB(230, 230, 250) }, { "lavenderblush", NSVG_RGB(255, 240, 245) }, { "lawngreen", NSVG_RGB(124, 252, 0) }, { "lemonchiffon", NSVG_RGB(255, 250, 205) }, { "lightblue", NSVG_RGB(173, 216, 230) }, { "lightcoral", NSVG_RGB(240, 128, 128) }, { "lightcyan", NSVG_RGB(224, 255, 255) }, { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) }, { "lightgray", NSVG_RGB(211, 211, 211) }, { "lightgreen", NSVG_RGB(144, 238, 144) }, { "lightgrey", NSVG_RGB(211, 211, 211) }, { "lightpink", NSVG_RGB(255, 182, 193) }, { "lightsalmon", NSVG_RGB(255, 160, 122) }, { "lightseagreen", NSVG_RGB( 32, 178, 170) }, { "lightskyblue", NSVG_RGB(135, 206, 250) }, { "lightslategray", NSVG_RGB(119, 136, 153) }, { "lightslategrey", NSVG_RGB(119, 136, 153) }, { "lightsteelblue", NSVG_RGB(176, 196, 222) }, { "lightyellow", NSVG_RGB(255, 255, 224) }, { "lime", NSVG_RGB( 0, 255, 0) }, { "limegreen", NSVG_RGB( 50, 205, 50) }, { "linen", NSVG_RGB(250, 240, 230) }, { "maroon", NSVG_RGB(128, 0, 0) }, { "mediumaquamarine", NSVG_RGB(102, 205, 170) }, { "mediumblue", NSVG_RGB( 0, 0, 205) }, { "mediumorchid", NSVG_RGB(186, 85, 211) }, { "mediumpurple", NSVG_RGB(147, 112, 219) }, { "mediumseagreen", NSVG_RGB( 60, 179, 113) }, { "mediumslateblue", NSVG_RGB(123, 104, 238) }, { "mediumspringgreen", NSVG_RGB( 0, 250, 154) }, { "mediumturquoise", NSVG_RGB( 72, 209, 204) }, { "mediumvioletred", NSVG_RGB(199, 21, 133) }, { "midnightblue", NSVG_RGB( 25, 25, 112) }, { "mintcream", NSVG_RGB(245, 255, 250) }, { "mistyrose", NSVG_RGB(255, 228, 225) }, { "moccasin", NSVG_RGB(255, 228, 181) }, { "navajowhite", NSVG_RGB(255, 222, 173) }, { "navy", NSVG_RGB( 0, 0, 128) }, { "oldlace", NSVG_RGB(253, 245, 230) }, { "olive", NSVG_RGB(128, 128, 0) }, { "olivedrab", NSVG_RGB(107, 142, 35) }, { "orange", NSVG_RGB(255, 165, 0) }, { "orangered", NSVG_RGB(255, 69, 0) }, { "orchid", NSVG_RGB(218, 112, 214) }, { "palegoldenrod", NSVG_RGB(238, 232, 170) }, { "palegreen", NSVG_RGB(152, 251, 152) }, { "paleturquoise", NSVG_RGB(175, 238, 238) }, { "palevioletred", NSVG_RGB(219, 112, 147) }, { "papayawhip", NSVG_RGB(255, 239, 213) }, { "peachpuff", NSVG_RGB(255, 218, 185) }, { "peru", NSVG_RGB(205, 133, 63) }, { "pink", NSVG_RGB(255, 192, 203) }, { "plum", NSVG_RGB(221, 160, 221) }, { "powderblue", NSVG_RGB(176, 224, 230) }, { "purple", NSVG_RGB(128, 0, 128) }, { "rosybrown", NSVG_RGB(188, 143, 143) }, { "royalblue", NSVG_RGB( 65, 105, 225) }, { "saddlebrown", NSVG_RGB(139, 69, 19) }, { "salmon", NSVG_RGB(250, 128, 114) }, { "sandybrown", NSVG_RGB(244, 164, 96) }, { "seagreen", NSVG_RGB( 46, 139, 87) }, { "seashell", NSVG_RGB(255, 245, 238) }, { "sienna", NSVG_RGB(160, 82, 45) }, { "silver", NSVG_RGB(192, 192, 192) }, { "skyblue", NSVG_RGB(135, 206, 235) }, { "slateblue", NSVG_RGB(106, 90, 205) }, { "slategray", NSVG_RGB(112, 128, 144) }, { "slategrey", NSVG_RGB(112, 128, 144) }, { "snow", NSVG_RGB(255, 250, 250) }, { "springgreen", NSVG_RGB( 0, 255, 127) }, { "steelblue", NSVG_RGB( 70, 130, 180) }, { "tan", NSVG_RGB(210, 180, 140) }, { "teal", NSVG_RGB( 0, 128, 128) }, { "thistle", NSVG_RGB(216, 191, 216) }, { "tomato", NSVG_RGB(255, 99, 71) }, { "turquoise", NSVG_RGB( 64, 224, 208) }, { "violet", NSVG_RGB(238, 130, 238) }, { "wheat", NSVG_RGB(245, 222, 179) }, { "whitesmoke", NSVG_RGB(245, 245, 245) }, { "yellowgreen", NSVG_RGB(154, 205, 50) }, #endif }; static unsigned int nsvg__parseColorName(const char* str) { int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); for (i = 0; i < ncolors; i++) { if (strcmp(nsvg__colors[i].name, str) == 0) { return nsvg__colors[i].color; } } return NSVG_RGB(128, 128, 128); } static unsigned int nsvg__parseColor(const char* str) { size_t len = 0; while(*str == ' ') ++str; len = strlen(str); if (len >= 1 && *str == '#') return nsvg__parseColorHex(str); else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') return nsvg__parseColorRGB(str); return nsvg__parseColorName(str); } static float nsvg__parseOpacity(const char* str) { float val = nsvg__atof(str); if (val < 0.0f) val = 0.0f; if (val > 1.0f) val = 1.0f; return val; } static float nsvg__parseMiterLimit(const char* str) { float val = nsvg__atof(str); if (val < 0.0f) val = 0.0f; return val; } static int nsvg__parseUnits(const char* units) { if (units[0] == 'p' && units[1] == 'x') return NSVG_UNITS_PX; else if (units[0] == 'p' && units[1] == 't') return NSVG_UNITS_PT; else if (units[0] == 'p' && units[1] == 'c') return NSVG_UNITS_PC; else if (units[0] == 'm' && units[1] == 'm') return NSVG_UNITS_MM; else if (units[0] == 'c' && units[1] == 'm') return NSVG_UNITS_CM; else if (units[0] == 'i' && units[1] == 'n') return NSVG_UNITS_IN; else if (units[0] == '%') return NSVG_UNITS_PERCENT; else if (units[0] == 'e' && units[1] == 'm') return NSVG_UNITS_EM; else if (units[0] == 'e' && units[1] == 'x') return NSVG_UNITS_EX; return NSVG_UNITS_USER; } static int nsvg__isCoordinate(const char* s) { // optional sign if (*s == '-' || *s == '+') s++; // must have at least one digit, or start by a dot return (nsvg__isdigit(*s) || *s == '.'); } static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) { NSVGcoordinate coord = {0, NSVG_UNITS_USER}; char buf[64]; coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); coord.value = nsvg__atof(buf); return coord; } static NSVGcoordinate nsvg__coord(float v, int units) { NSVGcoordinate coord ; coord.value = v; coord.units = units; return coord; } static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length) { NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); return nsvg__convertToPixels(p, coord, orig, length); } static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na) { const char* end; const char* ptr; char it[64]; *na = 0; ptr = str; while (*ptr && *ptr != '(') ++ptr; if (*ptr == 0) return 1; end = ptr; while (*end && *end != ')') ++end; if (*end == 0) return 1; while (ptr < end) { if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { if (*na >= maxNa) return 0; ptr = nsvg__parseNumber(ptr, it, 64); args[(*na)++] = (float)nsvg__atof(it); } else { ++ptr; } } return (int)(end - str); } static int nsvg__parseMatrix(float* xform, const char* str) { float t[6]; int na = 0; int len = nsvg__parseTransformArgs(str, t, 6, &na); if (na != 6) return len; memcpy(xform, t, sizeof(float)*6); return len; } static int nsvg__parseTranslate(float* xform, const char* str) { float args[2]; float t[6]; int na = 0; int len = nsvg__parseTransformArgs(str, args, 2, &na); if (na == 1) args[1] = 0.0; nsvg__xformSetTranslation(t, args[0], args[1]); memcpy(xform, t, sizeof(float)*6); return len; } static int nsvg__parseScale(float* xform, const char* str) { float args[2]; int na = 0; float t[6]; int len = nsvg__parseTransformArgs(str, args, 2, &na); if (na == 1) args[1] = args[0]; nsvg__xformSetScale(t, args[0], args[1]); memcpy(xform, t, sizeof(float)*6); return len; } static int nsvg__parseSkewX(float* xform, const char* str) { float args[1]; int na = 0; float t[6]; int len = nsvg__parseTransformArgs(str, args, 1, &na); nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI); memcpy(xform, t, sizeof(float)*6); return len; } static int nsvg__parseSkewY(float* xform, const char* str) { float args[1]; int na = 0; float t[6]; int len = nsvg__parseTransformArgs(str, args, 1, &na); nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI); memcpy(xform, t, sizeof(float)*6); return len; } static int nsvg__parseRotate(float* xform, const char* str) { float args[3]; int na = 0; float m[6]; float t[6]; int len = nsvg__parseTransformArgs(str, args, 3, &na); if (na == 1) args[1] = args[2] = 0.0f; nsvg__xformIdentity(m); if (na > 1) { nsvg__xformSetTranslation(t, -args[1], -args[2]); nsvg__xformMultiply(m, t); } nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI); nsvg__xformMultiply(m, t); if (na > 1) { nsvg__xformSetTranslation(t, args[1], args[2]); nsvg__xformMultiply(m, t); } memcpy(xform, m, sizeof(float)*6); return len; } static void nsvg__parseTransform(float* xform, const char* str) { float t[6] = { 0 }; int len; nsvg__xformIdentity(xform); while (*str) { if (strncmp(str, "matrix", 6) == 0) len = nsvg__parseMatrix(t, str); else if (strncmp(str, "translate", 9) == 0) len = nsvg__parseTranslate(t, str); else if (strncmp(str, "scale", 5) == 0) len = nsvg__parseScale(t, str); else if (strncmp(str, "rotate", 6) == 0) len = nsvg__parseRotate(t, str); else if (strncmp(str, "skewX", 5) == 0) len = nsvg__parseSkewX(t, str); else if (strncmp(str, "skewY", 5) == 0) len = nsvg__parseSkewY(t, str); else{ ++str; continue; } if (len != 0) { str += len; } else { ++str; continue; } nsvg__xformPremultiply(xform, t); } } static void nsvg__parseUrl(char* id, const char* str) { int i = 0; str += 4; // "url("; if (*str && *str == '#') str++; while (i < 63 && *str && *str != ')') { id[i] = *str++; i++; } id[i] = '\0'; } static char nsvg__parseLineCap(const char* str) { if (strcmp(str, "butt") == 0) return NSVG_CAP_BUTT; else if (strcmp(str, "round") == 0) return NSVG_CAP_ROUND; else if (strcmp(str, "square") == 0) return NSVG_CAP_SQUARE; // TODO: handle inherit. return NSVG_CAP_BUTT; } static char nsvg__parseLineJoin(const char* str) { if (strcmp(str, "miter") == 0) return NSVG_JOIN_MITER; else if (strcmp(str, "round") == 0) return NSVG_JOIN_ROUND; else if (strcmp(str, "bevel") == 0) return NSVG_JOIN_BEVEL; // TODO: handle inherit. return NSVG_JOIN_MITER; } static char nsvg__parseFillRule(const char* str) { if (strcmp(str, "nonzero") == 0) return NSVG_FILLRULE_NONZERO; else if (strcmp(str, "evenodd") == 0) return NSVG_FILLRULE_EVENODD; // TODO: handle inherit. return NSVG_FILLRULE_NONZERO; } static const char* nsvg__getNextDashItem(const char* s, char* it) { int n = 0; it[0] = '\0'; // Skip white spaces and commas while (*s && (nsvg__isspace(*s) || *s == ',')) s++; // Advance until whitespace, comma or end. while (*s && (!nsvg__isspace(*s) && *s != ',')) { if (n < 63) it[n++] = *s; s++; } it[n++] = '\0'; return s; } static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray) { char item[64]; int count = 0, i; float sum = 0.0f; // Handle "none" if (str[0] == 'n') return 0; // Parse dashes while (*str) { str = nsvg__getNextDashItem(str, item); if (!*item) break; if (count < NSVG_MAX_DASHES) strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); } for (i = 0; i < count; i++) sum += strokeDashArray[i]; if (sum <= 1e-6f) count = 0; return count; } static void nsvg__parseStyle(NSVGparser* p, const char* str); static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) { float xform[6]; NSVGattrib* attr = nsvg__getAttr(p); if (!attr) return 0; if (strcmp(name, "style") == 0) { nsvg__parseStyle(p, value); } else if (strcmp(name, "display") == 0) { if (strcmp(value, "none") == 0) attr->visible = 0; // Don't reset ->visible on display:inline, one display:none hides the whole subtree } else if (strcmp(name, "fill") == 0) { if (strcmp(value, "none") == 0) { attr->hasFill = 0; } else if (strncmp(value, "url(", 4) == 0) { attr->hasFill = 2; nsvg__parseUrl(attr->fillGradient, value); } else { attr->hasFill = 1; attr->fillColor = nsvg__parseColor(value); } } else if (strcmp(name, "opacity") == 0) { attr->opacity = nsvg__parseOpacity(value); } else if (strcmp(name, "fill-opacity") == 0) { attr->fillOpacity = nsvg__parseOpacity(value); } else if (strcmp(name, "stroke") == 0) { if (strcmp(value, "none") == 0) { attr->hasStroke = 0; } else if (strncmp(value, "url(", 4) == 0) { attr->hasStroke = 2; nsvg__parseUrl(attr->strokeGradient, value); } else { attr->hasStroke = 1; attr->strokeColor = nsvg__parseColor(value); } } else if (strcmp(name, "stroke-width") == 0) { attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); } else if (strcmp(name, "stroke-dasharray") == 0) { attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); } else if (strcmp(name, "stroke-dashoffset") == 0) { attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); } else if (strcmp(name, "stroke-opacity") == 0) { attr->strokeOpacity = nsvg__parseOpacity(value); } else if (strcmp(name, "stroke-linecap") == 0) { attr->strokeLineCap = nsvg__parseLineCap(value); } else if (strcmp(name, "stroke-linejoin") == 0) { attr->strokeLineJoin = nsvg__parseLineJoin(value); } else if (strcmp(name, "stroke-miterlimit") == 0) { attr->miterLimit = nsvg__parseMiterLimit(value); } else if (strcmp(name, "fill-rule") == 0) { attr->fillRule = nsvg__parseFillRule(value); } else if (strcmp(name, "font-size") == 0) { attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); } else if (strcmp(name, "transform") == 0) { nsvg__parseTransform(xform, value); nsvg__xformPremultiply(attr->xform, xform); } else if (strcmp(name, "stop-color") == 0) { attr->stopColor = nsvg__parseColor(value); } else if (strcmp(name, "stop-opacity") == 0) { attr->stopOpacity = nsvg__parseOpacity(value); } else if (strcmp(name, "offset") == 0) { attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); } else if (strcmp(name, "id") == 0) { strncpy(attr->id, value, 63); attr->id[63] = '\0'; } else if (strcmp(name, "class") == 0) { NSVGstyles* style = p->styles; while (style) { if (strcmp(style->name + 1, value) == 0) { nsvg__parseStyle(p, style->description); } style = style->next; } } else { return 0; } return 1; } static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end) { const char* str; const char* val; char name[512]; char value[512]; int n; str = start; while (str < end && *str != ':') ++str; val = str; // Right Trim while (str > start && (*str == ':' || nsvg__isspace(*str))) --str; ++str; n = (int)(str - start); if (n > 511) n = 511; if (n) memcpy(name, start, n); name[n] = 0; while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val; n = (int)(end - val); if (n > 511) n = 511; if (n) memcpy(value, val, n); value[n] = 0; return nsvg__parseAttr(p, name, value); } static void nsvg__parseStyle(NSVGparser* p, const char* str) { const char* start; const char* end; while (*str) { // Left Trim while(*str && nsvg__isspace(*str)) ++str; start = str; while(*str && *str != ';') ++str; end = str; // Right Trim while (end > start && (*end == ';' || nsvg__isspace(*end))) --end; ++end; nsvg__parseNameValue(p, start, end); if (*str) ++str; } } static void nsvg__parseAttribs(NSVGparser* p, const char** attr) { int i; for (i = 0; attr[i]; i += 2) { if (strcmp(attr[i], "style") == 0) nsvg__parseStyle(p, attr[i + 1]); else nsvg__parseAttr(p, attr[i], attr[i + 1]); } } static int nsvg__getArgsPerElement(char cmd) { switch (cmd) { case 'v': case 'V': case 'h': case 'H': return 1; case 'm': case 'M': case 'l': case 'L': case 't': case 'T': return 2; case 'q': case 'Q': case 's': case 'S': return 4; case 'c': case 'C': return 6; case 'a': case 'A': return 7; case 'z': case 'Z': return 0; } return -1; } static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) { if (rel) { *cpx += args[0]; *cpy += args[1]; } else { *cpx = args[0]; *cpy = args[1]; } nsvg__moveTo(p, *cpx, *cpy); } static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) { if (rel) { *cpx += args[0]; *cpy += args[1]; } else { *cpx = args[0]; *cpy = args[1]; } nsvg__lineTo(p, *cpx, *cpy); } static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) { if (rel) *cpx += args[0]; else *cpx = args[0]; nsvg__lineTo(p, *cpx, *cpy); } static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) { if (rel) *cpy += args[0]; else *cpy = args[0]; nsvg__lineTo(p, *cpx, *cpy); } static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy, float* cpx2, float* cpy2, float* args, int rel) { float x2, y2, cx1, cy1, cx2, cy2; if (rel) { cx1 = *cpx + args[0]; cy1 = *cpy + args[1]; cx2 = *cpx + args[2]; cy2 = *cpy + args[3]; x2 = *cpx + args[4]; y2 = *cpy + args[5]; } else { cx1 = args[0]; cy1 = args[1]; cx2 = args[2]; cy2 = args[3]; x2 = args[4]; y2 = args[5]; } nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); *cpx2 = cx2; *cpy2 = cy2; *cpx = x2; *cpy = y2; } static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy, float* cpx2, float* cpy2, float* args, int rel) { float x1, y1, x2, y2, cx1, cy1, cx2, cy2; x1 = *cpx; y1 = *cpy; if (rel) { cx2 = *cpx + args[0]; cy2 = *cpy + args[1]; x2 = *cpx + args[2]; y2 = *cpy + args[3]; } else { cx2 = args[0]; cy2 = args[1]; x2 = args[2]; y2 = args[3]; } cx1 = 2*x1 - *cpx2; cy1 = 2*y1 - *cpy2; nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); *cpx2 = cx2; *cpy2 = cy2; *cpx = x2; *cpy = y2; } static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy, float* cpx2, float* cpy2, float* args, int rel) { float x1, y1, x2, y2, cx, cy; float cx1, cy1, cx2, cy2; x1 = *cpx; y1 = *cpy; if (rel) { cx = *cpx + args[0]; cy = *cpy + args[1]; x2 = *cpx + args[2]; y2 = *cpy + args[3]; } else { cx = args[0]; cy = args[1]; x2 = args[2]; y2 = args[3]; } // Convert to cubic bezier cx1 = x1 + 2.0f/3.0f*(cx - x1); cy1 = y1 + 2.0f/3.0f*(cy - y1); cx2 = x2 + 2.0f/3.0f*(cx - x2); cy2 = y2 + 2.0f/3.0f*(cy - y2); nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); *cpx2 = cx; *cpy2 = cy; *cpx = x2; *cpy = y2; } static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy, float* cpx2, float* cpy2, float* args, int rel) { float x1, y1, x2, y2, cx, cy; float cx1, cy1, cx2, cy2; x1 = *cpx; y1 = *cpy; if (rel) { x2 = *cpx + args[0]; y2 = *cpy + args[1]; } else { x2 = args[0]; y2 = args[1]; } cx = 2*x1 - *cpx2; cy = 2*y1 - *cpy2; // Convert to cubix bezier cx1 = x1 + 2.0f/3.0f*(cx - x1); cy1 = y1 + 2.0f/3.0f*(cy - y1); cx2 = x2 + 2.0f/3.0f*(cx - x2); cy2 = y2 + 2.0f/3.0f*(cy - y2); nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); *cpx2 = cx; *cpy2 = cy; *cpx = x2; *cpy = y2; } static float nsvg__sqr(float x) { return x*x; } static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); } static float nsvg__vecrat(float ux, float uy, float vx, float vy) { return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy)); } static float nsvg__vecang(float ux, float uy, float vx, float vy) { float r = nsvg__vecrat(ux,uy, vx,vy); if (r < -1.0f) r = -1.0f; if (r > 1.0f) r = 1.0f; return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r); } static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) { // Ported from canvg (https://code.google.com/p/canvg/) float rx, ry, rotx; float x1, y1, x2, y2, cx, cy, dx, dy, d; float x1p, y1p, cxp, cyp, s, sa, sb; float ux, uy, vx, vy, a1, da; float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; float sinrx, cosrx; int fa, fs; int i, ndivs; float hda, kappa; rx = fabsf(args[0]); // y radius ry = fabsf(args[1]); // x radius rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction x1 = *cpx; // start point y1 = *cpy; if (rel) { // end point x2 = *cpx + args[5]; y2 = *cpy + args[6]; } else { x2 = args[5]; y2 = args[6]; } dx = x1 - x2; dy = y1 - y2; d = sqrtf(dx*dx + dy*dy); if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { // The arc degenerates to a line nsvg__lineTo(p, x2, y2); *cpx = x2; *cpy = y2; return; } sinrx = sinf(rotx); cosrx = cosf(rotx); // Convert to center point parameterization. // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes // 1) Compute x1', y1' x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry); if (d > 1) { d = sqrtf(d); rx *= d; ry *= d; } // 2) Compute cx', cy' s = 0.0f; sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p); sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p); if (sa < 0.0f) sa = 0.0f; if (sb > 0.0f) s = sqrtf(sa / sb); if (fa == fs) s = -s; cxp = s * rx * y1p / ry; cyp = s * -ry * x1p / rx; // 3) Compute cx,cy from cx',cy' cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp; cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp; // 4) Calculate theta1, and delta theta. ux = (x1p - cxp) / rx; uy = (y1p - cyp) / ry; vx = (-x1p - cxp) / rx; vy = (-y1p - cyp) / ry; a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle da = nsvg__vecang(ux,uy, vx,vy); // Delta angle // if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; // if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; if (fs == 0 && da > 0) da -= 2 * NSVG_PI; else if (fs == 1 && da < 0) da += 2 * NSVG_PI; // Approximate the arc using cubic spline segments. t[0] = cosrx; t[1] = sinrx; t[2] = -sinrx; t[3] = cosrx; t[4] = cx; t[5] = cy; // Split arc into max 90 degree segments. // The loop assumes an iteration per end point (including start and end), this +1. ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f); hda = (da / (float)ndivs) / 2.0f; // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite) if ((hda < 1e-3f) && (hda > -1e-3f)) hda *= 0.5f; else hda = (1.0f - cosf(hda)) / sinf(hda); kappa = fabsf(4.0f / 3.0f * hda); if (da < 0.0f) kappa = -kappa; for (i = 0; i <= ndivs; i++) { a = a1 + da * ((float)i/(float)ndivs); dx = cosf(a); dy = sinf(a); nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent if (i > 0) nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y); px = x; py = y; ptanx = tanx; ptany = tany; } *cpx = x2; *cpy = y2; } static void nsvg__parsePath(NSVGparser* p, const char** attr) { const char* s = NULL; char cmd = '\0'; float args[10]; int nargs; int rargs = 0; char initPoint; float cpx, cpy, cpx2, cpy2; const char* tmp[4]; char closedFlag; int i; char item[64]; for (i = 0; attr[i]; i += 2) { if (strcmp(attr[i], "d") == 0) { s = attr[i + 1]; } else { tmp[0] = attr[i]; tmp[1] = attr[i + 1]; tmp[2] = 0; tmp[3] = 0; nsvg__parseAttribs(p, tmp); } } if (s) { nsvg__resetPath(p); cpx = 0; cpy = 0; cpx2 = 0; cpy2 = 0; initPoint = 0; closedFlag = 0; nargs = 0; while (*s) { item[0] = '\0'; if ((cmd == 'A' || cmd == 'a') && (nargs == 3 || nargs == 4)) s = nsvg__getNextPathItemWhenArcFlag(s, item); if (!*item) s = nsvg__getNextPathItem(s, item); if (!*item) break; if (cmd != '\0' && nsvg__isCoordinate(item)) { if (nargs < 10) args[nargs++] = (float)nsvg__atof(item); if (nargs >= rargs) { switch (cmd) { case 'm': case 'M': nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0); // Moveto can be followed by multiple coordinate pairs, // which should be treated as linetos. cmd = (cmd == 'm') ? 'l' : 'L'; rargs = nsvg__getArgsPerElement(cmd); cpx2 = cpx; cpy2 = cpy; initPoint = 1; break; case 'l': case 'L': nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); cpx2 = cpx; cpy2 = cpy; break; case 'H': case 'h': nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); cpx2 = cpx; cpy2 = cpy; break; case 'V': case 'v': nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); cpx2 = cpx; cpy2 = cpy; break; case 'C': case 'c': nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); break; case 'S': case 's': nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); break; case 'Q': case 'q': nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); break; case 'T': case 't': nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); break; case 'A': case 'a': nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); cpx2 = cpx; cpy2 = cpy; break; default: if (nargs >= 2) { cpx = args[nargs-2]; cpy = args[nargs-1]; cpx2 = cpx; cpy2 = cpy; } break; } nargs = 0; } } else { cmd = item[0]; if (cmd == 'M' || cmd == 'm') { // Commit path. if (p->npts > 0) nsvg__addPath(p, closedFlag); // Start new subpath. nsvg__resetPath(p); closedFlag = 0; nargs = 0; } else if (initPoint == 0) { // Do not allow other commands until initial point has been set (moveTo called once). cmd = '\0'; } if (cmd == 'Z' || cmd == 'z') { closedFlag = 1; // Commit path. if (p->npts > 0) { // Move current point to first point cpx = p->pts[0]; cpy = p->pts[1]; cpx2 = cpx; cpy2 = cpy; nsvg__addPath(p, closedFlag); } // Start new subpath. nsvg__resetPath(p); nsvg__moveTo(p, cpx, cpy); closedFlag = 0; nargs = 0; } rargs = nsvg__getArgsPerElement(cmd); if (rargs == -1) { // Command not recognized cmd = '\0'; rargs = 0; } } } // Commit path. if (p->npts) nsvg__addPath(p, closedFlag); } nsvg__addShape(p); } static void nsvg__parseRect(NSVGparser* p, const char** attr) { float x = 0.0f; float y = 0.0f; float w = 0.0f; float h = 0.0f; float rx = -1.0f; // marks not set float ry = -1.0f; int i; for (i = 0; attr[i]; i += 2) { if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)); if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)); if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); } } if (rx < 0.0f && ry > 0.0f) rx = ry; if (ry < 0.0f && rx > 0.0f) ry = rx; if (rx < 0.0f) rx = 0.0f; if (ry < 0.0f) ry = 0.0f; if (rx > w/2.0f) rx = w/2.0f; if (ry > h/2.0f) ry = h/2.0f; if (w != 0.0f && h != 0.0f) { nsvg__resetPath(p); if (rx < 0.00001f || ry < 0.0001f) { nsvg__moveTo(p, x, y); nsvg__lineTo(p, x+w, y); nsvg__lineTo(p, x+w, y+h); nsvg__lineTo(p, x, y+h); } else { // Rounded rectangle nsvg__moveTo(p, x+rx, y); nsvg__lineTo(p, x+w-rx, y); nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry); nsvg__lineTo(p, x+w, y+h-ry); nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h); nsvg__lineTo(p, x+rx, y+h); nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry); nsvg__lineTo(p, x, y+ry); nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); } nsvg__addPath(p, 1); nsvg__addShape(p); } } static void nsvg__parseCircle(NSVGparser* p, const char** attr) { float cx = 0.0f; float cy = 0.0f; float r = 0.0f; int i; for (i = 0; attr[i]; i += 2) { if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p))); } } if (r > 0.0f) { nsvg__resetPath(p); nsvg__moveTo(p, cx+r, cy); nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r); nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy); nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r); nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy); nsvg__addPath(p, 1); nsvg__addShape(p); } } static void nsvg__parseEllipse(NSVGparser* p, const char** attr) { float cx = 0.0f; float cy = 0.0f; float rx = 0.0f; float ry = 0.0f; int i; for (i = 0; attr[i]; i += 2) { if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); } } if (rx > 0.0f && ry > 0.0f) { nsvg__resetPath(p); nsvg__moveTo(p, cx+rx, cy); nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry); nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy); nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry); nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy); nsvg__addPath(p, 1); nsvg__addShape(p); } } static void nsvg__parseLine(NSVGparser* p, const char** attr) { float x1 = 0.0; float y1 = 0.0; float x2 = 0.0; float y2 = 0.0; int i; for (i = 0; attr[i]; i += 2) { if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); } } nsvg__resetPath(p); nsvg__moveTo(p, x1, y1); nsvg__lineTo(p, x2, y2); nsvg__addPath(p, 0); nsvg__addShape(p); } static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag) { int i; const char* s; float args[2]; int nargs, npts = 0; char item[64]; nsvg__resetPath(p); for (i = 0; attr[i]; i += 2) { if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { if (strcmp(attr[i], "points") == 0) { s = attr[i + 1]; nargs = 0; while (*s) { s = nsvg__getNextPathItem(s, item); args[nargs++] = (float)nsvg__atof(item); if (nargs >= 2) { if (npts == 0) nsvg__moveTo(p, args[0], args[1]); else nsvg__lineTo(p, args[0], args[1]); nargs = 0; npts++; } } } } } nsvg__addPath(p, (char)closeFlag); nsvg__addShape(p); } static void nsvg__parseSVG(NSVGparser* p, const char** attr) { int i; for (i = 0; attr[i]; i += 2) { if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { if (strcmp(attr[i], "width") == 0) { p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); } else if (strcmp(attr[i], "height") == 0) { p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); } else if (strcmp(attr[i], "viewBox") == 0) { const char *s = attr[i + 1]; char buf[64]; s = nsvg__parseNumber(s, buf, 64); p->viewMinx = nsvg__atof(buf); while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; if (!*s) return; s = nsvg__parseNumber(s, buf, 64); p->viewMiny = nsvg__atof(buf); while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; if (!*s) return; s = nsvg__parseNumber(s, buf, 64); p->viewWidth = nsvg__atof(buf); while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; if (!*s) return; s = nsvg__parseNumber(s, buf, 64); p->viewHeight = nsvg__atof(buf); } else if (strcmp(attr[i], "preserveAspectRatio") == 0) { if (strstr(attr[i + 1], "none") != 0) { // No uniform scaling p->alignType = NSVG_ALIGN_NONE; } else { // Parse X align if (strstr(attr[i + 1], "xMin") != 0) p->alignX = NSVG_ALIGN_MIN; else if (strstr(attr[i + 1], "xMid") != 0) p->alignX = NSVG_ALIGN_MID; else if (strstr(attr[i + 1], "xMax") != 0) p->alignX = NSVG_ALIGN_MAX; // Parse X align if (strstr(attr[i + 1], "yMin") != 0) p->alignY = NSVG_ALIGN_MIN; else if (strstr(attr[i + 1], "yMid") != 0) p->alignY = NSVG_ALIGN_MID; else if (strstr(attr[i + 1], "yMax") != 0) p->alignY = NSVG_ALIGN_MAX; // Parse meet/slice p->alignType = NSVG_ALIGN_MEET; if (strstr(attr[i + 1], "slice") != 0) p->alignType = NSVG_ALIGN_SLICE; } } } } } static void nsvg__parseGradient(NSVGparser* p, const char** attr, signed char type) { int i; NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData)); if (grad == NULL) return; memset(grad, 0, sizeof(NSVGgradientData)); grad->units = NSVG_OBJECT_SPACE; grad->type = type; if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); } nsvg__xformIdentity(grad->xform); for (i = 0; attr[i]; i += 2) { if (strcmp(attr[i], "id") == 0) { strncpy(grad->id, attr[i+1], 63); grad->id[63] = '\0'; } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { if (strcmp(attr[i], "gradientUnits") == 0) { if (strcmp(attr[i+1], "objectBoundingBox") == 0) grad->units = NSVG_OBJECT_SPACE; else grad->units = NSVG_USER_SPACE; } else if (strcmp(attr[i], "gradientTransform") == 0) { nsvg__parseTransform(grad->xform, attr[i + 1]); } else if (strcmp(attr[i], "cx") == 0) { grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); } else if (strcmp(attr[i], "cy") == 0) { grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); } else if (strcmp(attr[i], "r") == 0) { grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); } else if (strcmp(attr[i], "fx") == 0) { grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); } else if (strcmp(attr[i], "fy") == 0) { grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); } else if (strcmp(attr[i], "x1") == 0) { grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); } else if (strcmp(attr[i], "y1") == 0) { grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); } else if (strcmp(attr[i], "x2") == 0) { grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); } else if (strcmp(attr[i], "y2") == 0) { grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); } else if (strcmp(attr[i], "spreadMethod") == 0) { if (strcmp(attr[i+1], "pad") == 0) grad->spread = NSVG_SPREAD_PAD; else if (strcmp(attr[i+1], "reflect") == 0) grad->spread = NSVG_SPREAD_REFLECT; else if (strcmp(attr[i+1], "repeat") == 0) grad->spread = NSVG_SPREAD_REPEAT; } else if (strcmp(attr[i], "xlink:href") == 0) { const char *href = attr[i+1]; strncpy(grad->ref, href+1, 62); grad->ref[62] = '\0'; } } } grad->next = p->gradients; p->gradients = grad; } static void nsvg__parseGradientStop(NSVGparser* p, const char** attr) { NSVGattrib* curAttr = nsvg__getAttr(p); NSVGgradientData* grad; NSVGgradientStop* stop; int i, idx; curAttr->stopOffset = 0; curAttr->stopColor = 0; curAttr->stopOpacity = 1.0f; for (i = 0; attr[i]; i += 2) { nsvg__parseAttr(p, attr[i], attr[i + 1]); } // Add stop to the last gradient. grad = p->gradients; if (grad == NULL) return; grad->nstops++; grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops); if (grad->stops == NULL) return; // Insert idx = grad->nstops-1; for (i = 0; i < grad->nstops-1; i++) { if (curAttr->stopOffset < grad->stops[i].offset) { idx = i; break; } } if (idx != grad->nstops-1) { for (i = grad->nstops-1; i > idx; i--) grad->stops[i] = grad->stops[i-1]; } stop = &grad->stops[idx]; stop->color = curAttr->stopColor; stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24; stop->offset = curAttr->stopOffset; } static void nsvg__startElement(void* ud, const char* el, const char** attr) { NSVGparser* p = (NSVGparser*)ud; if (p->defsFlag) { // Skip everything but gradients and styles in defs if (strcmp(el, "linearGradient") == 0) { nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); } else if (strcmp(el, "radialGradient") == 0) { nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); } else if (strcmp(el, "stop") == 0) { nsvg__parseGradientStop(p, attr); } else if (strcmp(el, "style") == 0) { p->styleFlag = 1; } return; } if (strcmp(el, "g") == 0) { nsvg__pushAttr(p); nsvg__parseAttribs(p, attr); } else if (strcmp(el, "path") == 0) { if (p->pathFlag) // Do not allow nested paths. return; nsvg__pushAttr(p); nsvg__parsePath(p, attr); nsvg__popAttr(p); } else if (strcmp(el, "rect") == 0) { nsvg__pushAttr(p); nsvg__parseRect(p, attr); nsvg__popAttr(p); } else if (strcmp(el, "circle") == 0) { nsvg__pushAttr(p); nsvg__parseCircle(p, attr); nsvg__popAttr(p); } else if (strcmp(el, "ellipse") == 0) { nsvg__pushAttr(p); nsvg__parseEllipse(p, attr); nsvg__popAttr(p); } else if (strcmp(el, "line") == 0) { nsvg__pushAttr(p); nsvg__parseLine(p, attr); nsvg__popAttr(p); } else if (strcmp(el, "polyline") == 0) { nsvg__pushAttr(p); nsvg__parsePoly(p, attr, 0); nsvg__popAttr(p); } else if (strcmp(el, "polygon") == 0) { nsvg__pushAttr(p); nsvg__parsePoly(p, attr, 1); nsvg__popAttr(p); } else if (strcmp(el, "linearGradient") == 0) { nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); } else if (strcmp(el, "radialGradient") == 0) { nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); } else if (strcmp(el, "stop") == 0) { nsvg__parseGradientStop(p, attr); } else if (strcmp(el, "defs") == 0) { p->defsFlag = 1; } else if (strcmp(el, "svg") == 0) { nsvg__parseSVG(p, attr); } else if (strcmp(el, "style") == 0) { p->styleFlag = 1; } } static void nsvg__endElement(void* ud, const char* el) { NSVGparser* p = (NSVGparser*)ud; if (strcmp(el, "g") == 0) { nsvg__popAttr(p); } else if (strcmp(el, "path") == 0) { p->pathFlag = 0; } else if (strcmp(el, "defs") == 0) { p->defsFlag = 0; } else if (strcmp(el, "style") == 0) { p->styleFlag = 0; } } static char *nsvg__strndup(const char *s, size_t n) { char *result; size_t len = strlen(s); if (n < len) len = n; result = (char *)malloc(len + 1); if (!result) return NULL; result[len] = '\0'; return (char *)memcpy(result, s, len); } static void nsvg__content(void* ud, const char* s) { NSVGparser* p = (NSVGparser*)ud; if (p->styleFlag) { int state = 0; int class_count = 0; const char* start = s; while (*s) { char c = *s; if (state == 2) { if (c == '{') { start = s + 1; } else if (c == '}') { NSVGstyles *style = p->styles; while (class_count > 0) { style->description = nsvg__strndup(start, (size_t)(s - start)); style = style->next; --class_count; } state = 0; } } else if (nsvg__isspace(c) || c == '{' || c == ',') { if (state == 1) { if (*start == '.') { NSVGstyles* next = p->styles; p->styles = (NSVGstyles*)malloc(sizeof(NSVGstyles)); p->styles->description = NULL; p->styles->next = next; p->styles->name = nsvg__strndup(start, (size_t)(s - start)); ++class_count; } start = s + 1; state = c == ',' ? 0 : 2; } } else if (state == 0) { start = s; state = 1; } s++; } } } static void nsvg__imageBounds(NSVGparser* p, float* bounds) { NSVGshape* shape; shape = p->image->shapes; if (shape == NULL) { bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; return; } bounds[0] = shape->bounds[0]; bounds[1] = shape->bounds[1]; bounds[2] = shape->bounds[2]; bounds[3] = shape->bounds[3]; for (shape = shape->next; shape != NULL; shape = shape->next) { bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); } } static float nsvg__viewAlign(float content, float container, int type) { if (type == NSVG_ALIGN_MIN) return 0; else if (type == NSVG_ALIGN_MAX) return container - content; // mid return (container - content) * 0.5f; } static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy) { float t[6]; nsvg__xformSetTranslation(t, tx, ty); nsvg__xformMultiply (grad->xform, t); nsvg__xformSetScale(t, sx, sy); nsvg__xformMultiply (grad->xform, t); } static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) { NSVGshape* shape; NSVGpath* path; float tx, ty, sx, sy, us, bounds[4], t[6], avgs; int i; float* pt; // Guess image size if not set completely. nsvg__imageBounds(p, bounds); if (p->viewWidth == 0) { if (p->image->width > 0) { p->viewWidth = p->image->width; } else { p->viewMinx = bounds[0]; p->viewWidth = bounds[2] - bounds[0]; } } if (p->viewHeight == 0) { if (p->image->height > 0) { p->viewHeight = p->image->height; } else { p->viewMiny = bounds[1]; p->viewHeight = bounds[3] - bounds[1]; } } if (p->image->width == 0) p->image->width = p->viewWidth; if (p->image->height == 0) p->image->height = p->viewHeight; tx = -p->viewMinx; ty = -p->viewMiny; sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; // Unit scaling us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); // Fix aspect ratio if (p->alignType == NSVG_ALIGN_MEET) { // fit whole image into viewbox sx = sy = nsvg__minf(sx, sy); tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; } else if (p->alignType == NSVG_ALIGN_SLICE) { // fill whole viewbox with image sx = sy = nsvg__maxf(sx, sy); tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; } // Transform sx *= us; sy *= us; avgs = (sx+sy) / 2.0f; for (shape = p->image->shapes; shape != NULL; shape = shape->next) { shape->bounds[0] = (shape->bounds[0] + tx) * sx; shape->bounds[1] = (shape->bounds[1] + ty) * sy; shape->bounds[2] = (shape->bounds[2] + tx) * sx; shape->bounds[3] = (shape->bounds[3] + ty) * sy; for (path = shape->paths; path != NULL; path = path->next) { path->bounds[0] = (path->bounds[0] + tx) * sx; path->bounds[1] = (path->bounds[1] + ty) * sy; path->bounds[2] = (path->bounds[2] + tx) * sx; path->bounds[3] = (path->bounds[3] + ty) * sy; for (i =0; i < path->npts; i++) { pt = &path->pts[i*2]; pt[0] = (pt[0] + tx) * sx; pt[1] = (pt[1] + ty) * sy; } } if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy); memcpy(t, shape->fill.gradient->xform, sizeof(float)*6); nsvg__xformInverse(shape->fill.gradient->xform, t); } if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy); memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6); nsvg__xformInverse(shape->stroke.gradient->xform, t); } shape->strokeWidth *= avgs; shape->strokeDashOffset *= avgs; for (i = 0; i < shape->strokeDashCount; i++) shape->strokeDashArray[i] *= avgs; } } static void nsvg__createGradients(NSVGparser* p) { NSVGshape* shape; for (shape = p->image->shapes; shape != NULL; shape = shape->next) { if (shape->fill.type == NSVG_PAINT_UNDEF) { if (shape->fillGradient[0] != '\0') { float inv[6], localBounds[4]; nsvg__xformInverse(inv, shape->xform); nsvg__getLocalBounds(localBounds, shape, inv); shape->fill.gradient = nsvg__createGradient(p, shape->fillGradient, localBounds, shape->xform, &shape->fill.type); } if (shape->fill.type == NSVG_PAINT_UNDEF) { shape->fill.type = NSVG_PAINT_NONE; } } if (shape->stroke.type == NSVG_PAINT_UNDEF) { if (shape->strokeGradient[0] != '\0') { float inv[6], localBounds[4]; nsvg__xformInverse(inv, shape->xform); nsvg__getLocalBounds(localBounds, shape, inv); shape->stroke.gradient = nsvg__createGradient(p, shape->strokeGradient, localBounds, shape->xform, &shape->stroke.type); } if (shape->stroke.type == NSVG_PAINT_UNDEF) { shape->stroke.type = NSVG_PAINT_NONE; } } } } NSVG_EXPORT NSVGimage* nsvgParse(char* input, const char* units, float dpi) { NSVGparser* p; NSVGimage* ret = 0; p = nsvg__createParser(); if (p == NULL) { return NULL; } p->dpi = dpi; nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); // Create gradients after all definitions have been parsed nsvg__createGradients(p); // Scale to viewBox nsvg__scaleToViewbox(p, units); ret = p->image; p->image = NULL; nsvg__deleteParser(p); return ret; } #ifdef HAVE_STDIO_H NSVG_EXPORT NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi) { FILE* fp = NULL; size_t size; char* data = NULL; NSVGimage* image = NULL; fp = fopen(filename, "rb"); if (!fp) goto error; fseek(fp, 0, SEEK_END); size = ftell(fp); fseek(fp, 0, SEEK_SET); data = (char*)malloc(size+1); if (data == NULL) goto error; if (fread(data, 1, size, fp) != size) goto error; data[size] = '\0'; // Must be null terminated. fclose(fp); image = nsvgParse(data, units, dpi); free(data); return image; error: if (fp) fclose(fp); if (data) free(data); if (image) nsvgDelete(image); return NULL; } #endif /* HAVE_STDIO_H */ #if 0 NSVG_EXPORT NSVGpath* nsvgDuplicatePath(NSVGpath* p) { NSVGpath* res = NULL; if (p == NULL) return NULL; res = (NSVGpath*)malloc(sizeof(NSVGpath)); if (res == NULL) goto error; memset(res, 0, sizeof(NSVGpath)); res->pts = (float*)malloc(p->npts*2*sizeof(float)); if (res->pts == NULL) goto error; memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); res->npts = p->npts; memcpy(res->bounds, p->bounds, sizeof(p->bounds)); res->closed = p->closed; return res; error: if (res != NULL) { free(res->pts); free(res); } return NULL; } #endif NSVG_EXPORT void nsvgDelete(NSVGimage* image) { NSVGshape *snext, *shape; if (image == NULL) return; shape = image->shapes; while (shape != NULL) { snext = shape->next; nsvg__deletePaths(shape->paths); nsvg__deletePaint(&shape->fill); nsvg__deletePaint(&shape->stroke); free(shape); shape = snext; } free(image); } #endif // NANOSVG_IMPLEMENTATION #endif // NANOSVG_H SDL2_image-2.8.8/src/nanosvgrast.h0000664000000000000000000011347114727102233013644 0ustar00/* * Copyright (c) 2013-14 Mikko Mononen memon@inside.org * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * * The polygon rasterization is heavily based on stb_truetype rasterizer * by Sean Barrett - http://nothings.org/ * */ #ifndef NANOSVGRAST_H #define NANOSVGRAST_H #ifndef NSVG_EXPORT #define NSVG_EXPORT #endif #ifndef NANOSVGRAST_CPLUSPLUS #ifdef __cplusplus extern "C" { #endif #endif typedef struct NSVGrasterizer NSVGrasterizer; /* Example Usage: // Load SVG NSVGimage* image; image = nsvgParseFromFile("test.svg", "px", 96); // Create rasterizer (can be used to render multiple images). struct NSVGrasterizer* rast = nsvgCreateRasterizer(); // Allocate memory for image unsigned char* img = malloc(w*h*4); // Rasterize nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4); */ // Allocated rasterizer context. NSVG_EXPORT NSVGrasterizer* nsvgCreateRasterizer(void); // Rasterizes SVG image, returns RGBA image (non-premultiplied alpha) // r - pointer to rasterizer context // image - pointer to image to rasterize // tx,ty - image offset (applied after scaling) // scale - image scale // dst - pointer to destination image data, 4 bytes per pixel (RGBA) // w - width of the image to render // h - height of the image to render // stride - number of bytes per scaleline in the destination buffer NSVG_EXPORT void nsvgRasterize(NSVGrasterizer* r, NSVGimage* image, float tx, float ty, float scale, unsigned char* dst, int w, int h, int stride); // Deletes rasterizer context. NSVG_EXPORT void nsvgDeleteRasterizer(NSVGrasterizer*); #ifndef NANOSVGRAST_CPLUSPLUS #ifdef __cplusplus } #endif #endif #ifdef NANOSVGRAST_IMPLEMENTATION /* #include */ #define NSVG__SUBSAMPLES 5 #define NSVG__FIXSHIFT 10 #define NSVG__FIX (1 << NSVG__FIXSHIFT) #define NSVG__FIXMASK (NSVG__FIX-1) #define NSVG__MEMPAGE_SIZE 1024 typedef struct NSVGedge { float x0,y0, x1,y1; int dir; struct NSVGedge* next; } NSVGedge; typedef struct NSVGpoint { float x, y; float dx, dy; float len; float dmx, dmy; unsigned char flags; } NSVGpoint; typedef struct NSVGactiveEdge { int x,dx; float ey; int dir; struct NSVGactiveEdge *next; } NSVGactiveEdge; typedef struct NSVGmemPage { unsigned char mem[NSVG__MEMPAGE_SIZE]; int size; struct NSVGmemPage* next; } NSVGmemPage; typedef struct NSVGcachedPaint { signed char type; char spread; float xform[6]; unsigned int colors[256]; } NSVGcachedPaint; struct NSVGrasterizer { float px, py; float tessTol; float distTol; NSVGedge* edges; int nedges; int cedges; NSVGpoint* points; int npoints; int cpoints; NSVGpoint* points2; int npoints2; int cpoints2; NSVGactiveEdge* freelist; NSVGmemPage* pages; NSVGmemPage* curpage; unsigned char* scanline; int cscanline; unsigned char* bitmap; int width, height, stride; }; NSVG_EXPORT NSVGrasterizer* nsvgCreateRasterizer(void) { NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer)); if (r == NULL) goto error; memset(r, 0, sizeof(NSVGrasterizer)); r->tessTol = 0.25f; r->distTol = 0.01f; return r; error: nsvgDeleteRasterizer(r); return NULL; } NSVG_EXPORT void nsvgDeleteRasterizer(NSVGrasterizer* r) { NSVGmemPage* p; if (r == NULL) return; p = r->pages; while (p != NULL) { NSVGmemPage* next = p->next; free(p); p = next; } if (r->edges) free(r->edges); if (r->points) free(r->points); if (r->points2) free(r->points2); if (r->scanline) free(r->scanline); free(r); } static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur) { NSVGmemPage *newp; // If using existing chain, return the next page in chain if (cur != NULL && cur->next != NULL) { return cur->next; } // Alloc new page newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage)); if (newp == NULL) return NULL; memset(newp, 0, sizeof(NSVGmemPage)); // Add to linked list if (cur != NULL) cur->next = newp; else r->pages = newp; return newp; } static void nsvg__resetPool(NSVGrasterizer* r) { NSVGmemPage* p = r->pages; while (p != NULL) { p->size = 0; p = p->next; } r->curpage = r->pages; } static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size) { unsigned char* buf; if (size > NSVG__MEMPAGE_SIZE) return NULL; if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) { r->curpage = nsvg__nextPage(r, r->curpage); } buf = &r->curpage->mem[r->curpage->size]; r->curpage->size += size; return buf; } static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol) { float dx = x2 - x1; float dy = y2 - y1; return dx*dx + dy*dy < tol*tol; } static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags) { NSVGpoint* pt; if (r->npoints > 0) { pt = &r->points[r->npoints-1]; if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) { pt->flags = (unsigned char)(pt->flags | flags); return; } } if (r->npoints+1 > r->cpoints) { r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); if (r->points == NULL) return; } pt = &r->points[r->npoints]; pt->x = x; pt->y = y; pt->flags = (unsigned char)flags; r->npoints++; } static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt) { if (r->npoints+1 > r->cpoints) { r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); if (r->points == NULL) return; } r->points[r->npoints] = pt; r->npoints++; } static void nsvg__duplicatePoints(NSVGrasterizer* r) { if (r->npoints > r->cpoints2) { r->cpoints2 = r->npoints; r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2); if (r->points2 == NULL) return; } memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints); r->npoints2 = r->npoints; } static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1) { NSVGedge* e; // Skip horizontal edges if (y0 == y1) return; if (r->nedges+1 > r->cedges) { r->cedges = r->cedges > 0 ? r->cedges * 2 : 64; r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges); if (r->edges == NULL) return; } e = &r->edges[r->nedges]; r->nedges++; if (y0 < y1) { e->x0 = x0; e->y0 = y0; e->x1 = x1; e->y1 = y1; e->dir = 1; } else { e->x0 = x1; e->y0 = y1; e->x1 = x0; e->y1 = y0; e->dir = -1; } } static float nsvg__normalize(float *x, float* y) { float d = sqrtf((*x)*(*x) + (*y)*(*y)); if (d > 1e-6f) { float id = 1.0f / d; *x *= id; *y *= id; } return d; } static float nsvg__absf(float x) { return x < 0 ? -x : x; } static float nsvg__roundf(float x) { return (x >= 0) ? floorf(x + 0.5f) : ceilf(x - 0.5f); } static void nsvg__flattenCubicBez(NSVGrasterizer* r, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, int level, int type) { const int max_level = 10; float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; float dx,dy,d2,d3; if (level > max_level) return; x12 = (x1+x2)*0.5f; y12 = (y1+y2)*0.5f; x23 = (x2+x3)*0.5f; y23 = (y2+y3)*0.5f; x34 = (x3+x4)*0.5f; y34 = (y3+y4)*0.5f; x123 = (x12+x23)*0.5f; y123 = (y12+y23)*0.5f; dx = x4 - x1; dy = y4 - y1; d2 = nsvg__absf((x2 - x4) * dy - (y2 - y4) * dx); d3 = nsvg__absf((x3 - x4) * dy - (y3 - y4) * dx); if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) { nsvg__addPathPoint(r, x4, y4, type); return; } ++level; if (level > max_level) return; x234 = (x23+x34)*0.5f; y234 = (y23+y34)*0.5f; x1234 = (x123+x234)*0.5f; y1234 = (y123+y234)*0.5f; nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level, 0); nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level, type); } static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale) { int i, j; NSVGpath* path; for (path = shape->paths; path != NULL; path = path->next) { r->npoints = 0; // Flatten path nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); for (i = 0; i < path->npts-1; i += 3) { float* p = &path->pts[i*2]; nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0); } // Close path nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); // Build edges for (i = 0, j = r->npoints-1; i < r->npoints; j = i++) nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y); } } enum NSVGpointFlags { NSVG_PT_CORNER = 0x01, NSVG_PT_BEVEL = 0x02, NSVG_PT_LEFT = 0x04 }; static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) { float w = lineWidth * 0.5f; float dx = p1->x - p0->x; float dy = p1->y - p0->y; float len = nsvg__normalize(&dx, &dy); float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f; float dlx = dy, dly = -dx; float lx = px - dlx*w, ly = py - dly*w; float rx = px + dlx*w, ry = py + dly*w; left->x = lx; left->y = ly; right->x = rx; right->y = ry; } static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) { float w = lineWidth * 0.5f; float px = p->x, py = p->y; float dlx = dy, dly = -dx; float lx = px - dlx*w, ly = py - dly*w; float rx = px + dlx*w, ry = py + dly*w; nsvg__addEdge(r, lx, ly, rx, ry); if (connect) { nsvg__addEdge(r, left->x, left->y, lx, ly); nsvg__addEdge(r, rx, ry, right->x, right->y); } left->x = lx; left->y = ly; right->x = rx; right->y = ry; } static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) { float w = lineWidth * 0.5f; float px = p->x - dx*w, py = p->y - dy*w; float dlx = dy, dly = -dx; float lx = px - dlx*w, ly = py - dly*w; float rx = px + dlx*w, ry = py + dly*w; nsvg__addEdge(r, lx, ly, rx, ry); if (connect) { nsvg__addEdge(r, left->x, left->y, lx, ly); nsvg__addEdge(r, rx, ry, right->x, right->y); } left->x = lx; left->y = ly; right->x = rx; right->y = ry; } #ifndef NSVG_PI #define NSVG_PI (3.14159265358979323846264338327f) #endif static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect) { int i; float w = lineWidth * 0.5f; float px = p->x, py = p->y; float dlx = dy, dly = -dx; float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; for (i = 0; i < ncap; i++) { float a = (float)i/(float)(ncap-1)*NSVG_PI; float ax = cosf(a) * w, ay = sinf(a) * w; float x = px - dlx*ax - dx*ay; float y = py - dly*ax - dy*ay; if (i > 0) nsvg__addEdge(r, prevx, prevy, x, y); prevx = x; prevy = y; if (i == 0) { lx = x; ly = y; } else if (i == ncap-1) { rx = x; ry = y; } } if (connect) { nsvg__addEdge(r, left->x, left->y, lx, ly); nsvg__addEdge(r, rx, ry, right->x, right->y); } left->x = lx; left->y = ly; right->x = rx; right->y = ry; } static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) { float w = lineWidth * 0.5f; float dlx0 = p0->dy, dly0 = -p0->dx; float dlx1 = p1->dy, dly1 = -p1->dx; float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w); float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w); float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w); float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w); nsvg__addEdge(r, lx0, ly0, left->x, left->y); nsvg__addEdge(r, lx1, ly1, lx0, ly0); nsvg__addEdge(r, right->x, right->y, rx0, ry0); nsvg__addEdge(r, rx0, ry0, rx1, ry1); left->x = lx1; left->y = ly1; right->x = rx1; right->y = ry1; } static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) { float w = lineWidth * 0.5f; float dlx0 = p0->dy, dly0 = -p0->dx; float dlx1 = p1->dy, dly1 = -p1->dx; float lx0, rx0, lx1, rx1; float ly0, ry0, ly1, ry1; if (p1->flags & NSVG_PT_LEFT) { lx0 = lx1 = p1->x - p1->dmx * w; ly0 = ly1 = p1->y - p1->dmy * w; nsvg__addEdge(r, lx1, ly1, left->x, left->y); rx0 = p1->x + (dlx0 * w); ry0 = p1->y + (dly0 * w); rx1 = p1->x + (dlx1 * w); ry1 = p1->y + (dly1 * w); nsvg__addEdge(r, right->x, right->y, rx0, ry0); nsvg__addEdge(r, rx0, ry0, rx1, ry1); } else { lx0 = p1->x - (dlx0 * w); ly0 = p1->y - (dly0 * w); lx1 = p1->x - (dlx1 * w); ly1 = p1->y - (dly1 * w); nsvg__addEdge(r, lx0, ly0, left->x, left->y); nsvg__addEdge(r, lx1, ly1, lx0, ly0); rx0 = rx1 = p1->x + p1->dmx * w; ry0 = ry1 = p1->y + p1->dmy * w; nsvg__addEdge(r, right->x, right->y, rx1, ry1); } left->x = lx1; left->y = ly1; right->x = rx1; right->y = ry1; } static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap) { int i, n; float w = lineWidth * 0.5f; float dlx0 = p0->dy, dly0 = -p0->dx; float dlx1 = p1->dy, dly1 = -p1->dx; float a0 = atan2f(dly0, dlx0); float a1 = atan2f(dly1, dlx1); float da = a1 - a0; float lx, ly, rx, ry; if (da < NSVG_PI) da += NSVG_PI*2; if (da > NSVG_PI) da -= NSVG_PI*2; n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap); if (n < 2) n = 2; if (n > ncap) n = ncap; lx = left->x; ly = left->y; rx = right->x; ry = right->y; for (i = 0; i < n; i++) { float u = (float)i/(float)(n-1); float a = a0 + u*da; float ax = cosf(a) * w, ay = sinf(a) * w; float lx1 = p1->x - ax, ly1 = p1->y - ay; float rx1 = p1->x + ax, ry1 = p1->y + ay; nsvg__addEdge(r, lx1, ly1, lx, ly); nsvg__addEdge(r, rx, ry, rx1, ry1); lx = lx1; ly = ly1; rx = rx1; ry = ry1; } left->x = lx; left->y = ly; right->x = rx; right->y = ry; } static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth) { float w = lineWidth * 0.5f; float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w); float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w); nsvg__addEdge(r, lx, ly, left->x, left->y); nsvg__addEdge(r, right->x, right->y, rx, ry); left->x = lx; left->y = ly; right->x = rx; right->y = ry; } static int nsvg__curveDivs(float r, float arc, float tol) { float da = acosf(r / (r + tol)) * 2.0f; int divs = (int)ceilf(arc / da); if (divs < 2) divs = 2; return divs; } static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth) { int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle. NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0}; NSVGpoint* p0, *p1; int j, s, e; // Build stroke edges if (closed) { // Looping p0 = &points[npoints-1]; p1 = &points[0]; s = 0; e = npoints; } else { // Add cap p0 = &points[0]; p1 = &points[1]; s = 1; e = npoints-1; } if (closed) { nsvg__initClosed(&left, &right, p0, p1, lineWidth); firstLeft = left; firstRight = right; } else { // Add cap float dx = p1->x - p0->x; float dy = p1->y - p0->y; nsvg__normalize(&dx, &dy); if (lineCap == NSVG_CAP_BUTT) nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0); else if (lineCap == NSVG_CAP_SQUARE) nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0); else if (lineCap == NSVG_CAP_ROUND) nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0); } for (j = s; j < e; ++j) { if (p1->flags & NSVG_PT_CORNER) { if (lineJoin == NSVG_JOIN_ROUND) nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL)) nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); else nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); } else { nsvg__straightJoin(r, &left, &right, p1, lineWidth); } p0 = p1++; } if (closed) { // Loop it nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y); nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y); } else { // Add cap float dx = p1->x - p0->x; float dy = p1->y - p0->y; nsvg__normalize(&dx, &dy); if (lineCap == NSVG_CAP_BUTT) nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); else if (lineCap == NSVG_CAP_SQUARE) nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); else if (lineCap == NSVG_CAP_ROUND) nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1); } } static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin) { int i, j; NSVGpoint* p0, *p1; p0 = &r->points[r->npoints-1]; p1 = &r->points[0]; for (i = 0; i < r->npoints; i++) { // Calculate segment direction and length p0->dx = p1->x - p0->x; p0->dy = p1->y - p0->y; p0->len = nsvg__normalize(&p0->dx, &p0->dy); // Advance p0 = p1++; } // calculate joins p0 = &r->points[r->npoints-1]; p1 = &r->points[0]; for (j = 0; j < r->npoints; j++) { float dlx0, dly0, dlx1, dly1, dmr2, cross; dlx0 = p0->dy; dly0 = -p0->dx; dlx1 = p1->dy; dly1 = -p1->dx; // Calculate extrusions p1->dmx = (dlx0 + dlx1) * 0.5f; p1->dmy = (dly0 + dly1) * 0.5f; dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; if (dmr2 > 0.000001f) { float s2 = 1.0f / dmr2; if (s2 > 600.0f) { s2 = 600.0f; } p1->dmx *= s2; p1->dmy *= s2; } // Clear flags, but keep the corner. p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0; // Keep track of left turns. cross = p1->dx * p0->dy - p0->dx * p1->dy; if (cross > 0.0f) p1->flags |= NSVG_PT_LEFT; // Check to see if the corner needs to be beveled. if (p1->flags & NSVG_PT_CORNER) { if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) { p1->flags |= NSVG_PT_BEVEL; } } p0 = p1++; } } static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale) { int i, j, closed; NSVGpath* path; NSVGpoint* p0, *p1; float miterLimit = shape->miterLimit; int lineJoin = shape->strokeLineJoin; int lineCap = shape->strokeLineCap; float lineWidth = shape->strokeWidth * scale; for (path = shape->paths; path != NULL; path = path->next) { // Flatten path r->npoints = 0; nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER); for (i = 0; i < path->npts-1; i += 3) { float* p = &path->pts[i*2]; nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER); } if (r->npoints < 2) continue; closed = path->closed; // If the first and last points are the same, remove the last, mark as closed path. p0 = &r->points[r->npoints-1]; p1 = &r->points[0]; if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) { r->npoints--; p0 = &r->points[r->npoints-1]; closed = 1; } if (shape->strokeDashCount > 0) { int idash = 0, dashState = 1; float totalDist = 0, dashLen, allDashLen, dashOffset; NSVGpoint cur; if (closed) nsvg__appendPathPoint(r, r->points[0]); // Duplicate points -> points2. nsvg__duplicatePoints(r); r->npoints = 0; cur = r->points2[0]; nsvg__appendPathPoint(r, cur); // Figure out dash offset. allDashLen = 0; for (j = 0; j < shape->strokeDashCount; j++) allDashLen += shape->strokeDashArray[j]; if (shape->strokeDashCount & 1) allDashLen *= 2.0f; // Find location inside pattern dashOffset = fmodf(shape->strokeDashOffset, allDashLen); if (dashOffset < 0.0f) dashOffset += allDashLen; while (dashOffset > shape->strokeDashArray[idash]) { dashOffset -= shape->strokeDashArray[idash]; idash = (idash + 1) % shape->strokeDashCount; } dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale; for (j = 1; j < r->npoints2; ) { float dx = r->points2[j].x - cur.x; float dy = r->points2[j].y - cur.y; float dist = sqrtf(dx*dx + dy*dy); if ((totalDist + dist) > dashLen) { // Calculate intermediate point float d = (dashLen - totalDist) / dist; float x = cur.x + dx * d; float y = cur.y + dy * d; nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER); // Stroke if (r->npoints > 1 && dashState) { nsvg__prepareStroke(r, miterLimit, lineJoin); nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); } // Advance dash pattern dashState = !dashState; idash = (idash+1) % shape->strokeDashCount; dashLen = shape->strokeDashArray[idash] * scale; // Restart cur.x = x; cur.y = y; cur.flags = NSVG_PT_CORNER; totalDist = 0.0f; r->npoints = 0; nsvg__appendPathPoint(r, cur); } else { totalDist += dist; cur = r->points2[j]; nsvg__appendPathPoint(r, cur); j++; } } // Stroke any leftover path if (r->npoints > 1 && dashState) nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); } else { nsvg__prepareStroke(r, miterLimit, lineJoin); nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth); } } } static int SDLCALL nsvg__cmpEdge(const void *p, const void *q) { const NSVGedge* a = (const NSVGedge*)p; const NSVGedge* b = (const NSVGedge*)q; if (a->y0 < b->y0) return -1; if (a->y0 > b->y0) return 1; return 0; } static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint) { NSVGactiveEdge* z; float dxdy; if (r->freelist != NULL) { // Restore from freelist. z = r->freelist; r->freelist = z->next; } else { // Alloc new edge. z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge)); if (z == NULL) return NULL; } dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); // STBTT_assert(e->y0 <= start_point); // round dx down to avoid going too far if (dxdy < 0) z->dx = (int)(-nsvg__roundf(NSVG__FIX * -dxdy)); else z->dx = (int)nsvg__roundf(NSVG__FIX * dxdy); z->x = (int)nsvg__roundf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0))); // z->x -= off_x * FIX; z->ey = e->y1; z->next = 0; z->dir = e->dir; return z; } static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z) { z->next = r->freelist; r->freelist = z; } static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax) { int i = x0 >> NSVG__FIXSHIFT; int j = x1 >> NSVG__FIXSHIFT; if (i < *xmin) *xmin = i; if (j > *xmax) *xmax = j; if (i < len && j >= 0) { if (i == j) { // x0,x1 are the same pixel, so compute combined coverage scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT)); } else { if (i >= 0) // add antialiasing for x0 scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT)); else i = -1; // clip if (j < len) // add antialiasing for x1 scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT)); else j = len; // clip for (++i; i < j; ++i) // fill pixels between x0 and x1 scanline[i] = (unsigned char)(scanline[i] + maxWeight); } } } // note: this routine clips fills that extend off the edges... ideally this // wouldn't happen, but it could happen if the truetype glyph bounding boxes // are wrong, or if the user supplies a too-small bitmap static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule) { // non-zero winding fill int x0 = 0, w = 0; if (fillRule == NSVG_FILLRULE_NONZERO) { // Non-zero while (e != NULL) { if (w == 0) { // if we're currently at zero, we need to record the edge start point x0 = e->x; w += e->dir; } else { int x1 = e->x; w += e->dir; // if we went to zero, we need to draw if (w == 0) nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); } e = e->next; } } else if (fillRule == NSVG_FILLRULE_EVENODD) { // Even-odd while (e != NULL) { if (w == 0) { // if we're currently at zero, we need to record the edge start point x0 = e->x; w = 1; } else { int x1 = e->x; w = 0; nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); } e = e->next; } } } static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { return ((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16) | ((unsigned int)a << 24); } static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u) { int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8; int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8; int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8; int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8; return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); } static unsigned int nsvg__applyOpacity(unsigned int c, float u) { int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); int r = (c) & 0xff; int g = (c>>8) & 0xff; int b = (c>>16) & 0xff; int a = (((c>>24) & 0xff)*iu) >> 8; return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); } static int nsvg__div255(int x) { return ((x+1) * 257) >> 16; } static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y, float tx, float ty, float scale, NSVGcachedPaint* cache) { if (cache->type == NSVG_PAINT_COLOR) { int i, cr, cg, cb, ca; cr = cache->colors[0] & 0xff; cg = (cache->colors[0] >> 8) & 0xff; cb = (cache->colors[0] >> 16) & 0xff; ca = (cache->colors[0] >> 24) & 0xff; for (i = 0; i < count; i++) { int r,g,b; int a = nsvg__div255((int)cover[0] * ca); int ia = 255 - a; // Premultiply r = nsvg__div255(cr * a); g = nsvg__div255(cg * a); b = nsvg__div255(cb * a); // Blend over r += nsvg__div255(ia * (int)dst[0]); g += nsvg__div255(ia * (int)dst[1]); b += nsvg__div255(ia * (int)dst[2]); a += nsvg__div255(ia * (int)dst[3]); dst[0] = (unsigned char)r; dst[1] = (unsigned char)g; dst[2] = (unsigned char)b; dst[3] = (unsigned char)a; cover++; dst += 4; } } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) { // TODO: spread modes. // TODO: plenty of opportunities to optimize. float fx, fy, dx, gy; float* t = cache->xform; int i, cr, cg, cb, ca; unsigned int c; fx = ((float)x - tx) / scale; fy = ((float)y - ty) / scale; dx = 1.0f / scale; for (i = 0; i < count; i++) { int r,g,b,a,ia; gy = fx*t[1] + fy*t[3] + t[5]; c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)]; cr = (c) & 0xff; cg = (c >> 8) & 0xff; cb = (c >> 16) & 0xff; ca = (c >> 24) & 0xff; a = nsvg__div255((int)cover[0] * ca); ia = 255 - a; // Premultiply r = nsvg__div255(cr * a); g = nsvg__div255(cg * a); b = nsvg__div255(cb * a); // Blend over r += nsvg__div255(ia * (int)dst[0]); g += nsvg__div255(ia * (int)dst[1]); b += nsvg__div255(ia * (int)dst[2]); a += nsvg__div255(ia * (int)dst[3]); dst[0] = (unsigned char)r; dst[1] = (unsigned char)g; dst[2] = (unsigned char)b; dst[3] = (unsigned char)a; cover++; dst += 4; fx += dx; } } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) { // TODO: spread modes. // TODO: plenty of opportunities to optimize. // TODO: focus (fx,fy) float fx, fy, dx, gx, gy, gd; float* t = cache->xform; int i, cr, cg, cb, ca; unsigned int c; fx = ((float)x - tx) / scale; fy = ((float)y - ty) / scale; dx = 1.0f / scale; for (i = 0; i < count; i++) { int r,g,b,a,ia; gx = fx*t[0] + fy*t[2] + t[4]; gy = fx*t[1] + fy*t[3] + t[5]; gd = sqrtf(gx*gx + gy*gy); c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)]; cr = (c) & 0xff; cg = (c >> 8) & 0xff; cb = (c >> 16) & 0xff; ca = (c >> 24) & 0xff; a = nsvg__div255((int)cover[0] * ca); ia = 255 - a; // Premultiply r = nsvg__div255(cr * a); g = nsvg__div255(cg * a); b = nsvg__div255(cb * a); // Blend over r += nsvg__div255(ia * (int)dst[0]); g += nsvg__div255(ia * (int)dst[1]); b += nsvg__div255(ia * (int)dst[2]); a += nsvg__div255(ia * (int)dst[3]); dst[0] = (unsigned char)r; dst[1] = (unsigned char)g; dst[2] = (unsigned char)b; dst[3] = (unsigned char)a; cover++; dst += 4; fx += dx; } } } static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule) { NSVGactiveEdge *active = NULL; int y, s; int e = 0; int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline int xmin, xmax; for (y = 0; y < r->height; y++) { memset(r->scanline, 0, r->width); xmin = r->width; xmax = 0; for (s = 0; s < NSVG__SUBSAMPLES; ++s) { // find center of pixel for this scanline float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f; NSVGactiveEdge **step = &active; // update all active edges; // remove all active edges that terminate before the center of this scanline while (*step) { NSVGactiveEdge *z = *step; if (z->ey <= scany) { *step = z->next; // delete from list // NSVG__assert(z->valid); nsvg__freeActive(r, z); } else { z->x += z->dx; // advance to position for current scanline step = &((*step)->next); // advance through list } } // resort the list if needed for (;;) { int changed = 0; step = &active; while (*step && (*step)->next) { if ((*step)->x > (*step)->next->x) { NSVGactiveEdge* t = *step; NSVGactiveEdge* q = t->next; t->next = q->next; q->next = t; *step = q; changed = 1; } step = &(*step)->next; } if (!changed) break; } // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline while (e < r->nedges && r->edges[e].y0 <= scany) { if (r->edges[e].y1 > scany) { NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany); if (z == NULL) break; // find insertion point if (active == NULL) { active = z; } else if (z->x < active->x) { // insert at front z->next = active; active = z; } else { // find thing to insert AFTER NSVGactiveEdge* p = active; while (p->next && p->next->x < z->x) p = p->next; // at this point, p->next->x is NOT < z->x z->next = p->next; p->next = z; } } e++; } // now process all active edges in non-zero fashion if (active != NULL) nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule); } // Blit if (xmin < 0) xmin = 0; if (xmax > r->width-1) xmax = r->width-1; if (xmin <= xmax) { nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache); } } } static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride) { int x,y; // Unpremultiply for (y = 0; y < h; y++) { unsigned char *row = &image[y*stride]; for (x = 0; x < w; x++) { int r = row[0], g = row[1], b = row[2], a = row[3]; if (a != 0) { row[0] = (unsigned char)(r*255/a); row[1] = (unsigned char)(g*255/a); row[2] = (unsigned char)(b*255/a); } row += 4; } } // Defringe for (y = 0; y < h; y++) { unsigned char *row = &image[y*stride]; for (x = 0; x < w; x++) { int r = 0, g = 0, b = 0, a = row[3], n = 0; if (a == 0) { if (x-1 > 0 && row[-1] != 0) { r += row[-4]; g += row[-3]; b += row[-2]; n++; } if (x+1 < w && row[7] != 0) { r += row[4]; g += row[5]; b += row[6]; n++; } if (y-1 > 0 && row[-stride+3] != 0) { r += row[-stride]; g += row[-stride+1]; b += row[-stride+2]; n++; } if (y+1 < h && row[stride+3] != 0) { r += row[stride]; g += row[stride+1]; b += row[stride+2]; n++; } if (n > 0) { row[0] = (unsigned char)(r/n); row[1] = (unsigned char)(g/n); row[2] = (unsigned char)(b/n); } } row += 4; } } } static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity) { int i, j; NSVGgradient* grad; cache->type = paint->type; if (paint->type == NSVG_PAINT_COLOR) { cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); return; } grad = paint->gradient; cache->spread = grad->spread; memcpy(cache->xform, grad->xform, sizeof(float)*6); if (grad->nstops == 0) { for (i = 0; i < 256; i++) cache->colors[i] = 0; } else if (grad->nstops == 1) { unsigned int color = nsvg__applyOpacity(grad->stops[0].color, opacity); for (i = 0; i < 256; i++) cache->colors[i] = color; } else { unsigned int ca, cb = 0; float ua, ub, du, u; int ia, ib, count; ca = nsvg__applyOpacity(grad->stops[0].color, opacity); ua = nsvg__clampf(grad->stops[0].offset, 0, 1); ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1); ia = (int)(ua * 255.0f); ib = (int)(ub * 255.0f); for (i = 0; i < ia; i++) { cache->colors[i] = ca; } for (i = 0; i < grad->nstops-1; i++) { ca = nsvg__applyOpacity(grad->stops[i].color, opacity); cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity); ua = nsvg__clampf(grad->stops[i].offset, 0, 1); ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1); ia = (int)(ua * 255.0f); ib = (int)(ub * 255.0f); count = ib - ia; if (count <= 0) continue; u = 0; du = 1.0f / (float)count; for (j = 0; j < count; j++) { cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u); u += du; } } for (i = ib; i < 256; i++) cache->colors[i] = cb; } } /* static void dumpEdges(NSVGrasterizer* r, const char* name) { float xmin = 0, xmax = 0, ymin = 0, ymax = 0; NSVGedge *e = NULL; int i; if (r->nedges == 0) return; FILE* fp = fopen(name, "w"); if (fp == NULL) return; xmin = xmax = r->edges[0].x0; ymin = ymax = r->edges[0].y0; for (i = 0; i < r->nedges; i++) { e = &r->edges[i]; xmin = nsvg__minf(xmin, e->x0); xmin = nsvg__minf(xmin, e->x1); xmax = nsvg__maxf(xmax, e->x0); xmax = nsvg__maxf(xmax, e->x1); ymin = nsvg__minf(ymin, e->y0); ymin = nsvg__minf(ymin, e->y1); ymax = nsvg__maxf(ymax, e->y0); ymax = nsvg__maxf(ymax, e->y1); } fprintf(fp, "", xmin, ymin, (xmax - xmin), (ymax - ymin)); for (i = 0; i < r->nedges; i++) { e = &r->edges[i]; fprintf(fp ,"", e->x0,e->y0, e->x1,e->y1); } for (i = 0; i < r->npoints; i++) { if (i+1 < r->npoints) fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); } fprintf(fp, ""); fclose(fp); } */ NSVG_EXPORT void nsvgRasterize(NSVGrasterizer* r, NSVGimage* image, float tx, float ty, float scale, unsigned char* dst, int w, int h, int stride) { NSVGshape *shape = NULL; NSVGedge *e = NULL; NSVGcachedPaint cache; int i; r->bitmap = dst; r->width = w; r->height = h; r->stride = stride; if (w > r->cscanline) { r->cscanline = w; r->scanline = (unsigned char*)realloc(r->scanline, w); if (r->scanline == NULL) return; } for (i = 0; i < h; i++) memset(&dst[i*stride], 0, w*4); for (shape = image->shapes; shape != NULL; shape = shape->next) { if (!(shape->flags & NSVG_FLAGS_VISIBLE)) continue; if (shape->fill.type != NSVG_PAINT_NONE) { nsvg__resetPool(r); r->freelist = NULL; r->nedges = 0; nsvg__flattenShape(r, shape, scale); // Scale and translate edges for (i = 0; i < r->nedges; i++) { e = &r->edges[i]; e->x0 = tx + e->x0; e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; e->x1 = tx + e->x1; e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; } // Rasterize edges if (r->nedges != 0) qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule nsvg__initPaint(&cache, &shape->fill, shape->opacity); nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule); } if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) { nsvg__resetPool(r); r->freelist = NULL; r->nedges = 0; nsvg__flattenShapeStroke(r, shape, scale); // dumpEdges(r, "edge.svg"); // Scale and translate edges for (i = 0; i < r->nedges; i++) { e = &r->edges[i]; e->x0 = tx + e->x0; e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; e->x1 = tx + e->x1; e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; } // Rasterize edges if (r->nedges != 0) qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule nsvg__initPaint(&cache, &shape->stroke, shape->opacity); nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO); } } nsvg__unpremultiplyAlpha(dst, w, h, stride); r->bitmap = NULL; r->width = 0; r->height = 0; r->stride = 0; } #endif // NANOSVGRAST_IMPLEMENTATION #endif // NANOSVGRAST_H SDL2_image-2.8.8/src/qoi.h0000664000000000000000000004552114444663624012103 0ustar00/* QOI - The "Quite OK Image" format for fast, lossless image compression Dominic Szablewski - https://phoboslab.org -- LICENSE: The MIT License(MIT) Copyright(c) 2021 Dominic Szablewski 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. -- About QOI encodes and decodes images in a lossless format. Compared to stb_image and stb_image_write QOI offers 20x-50x faster encoding, 3x-4x faster decoding and 20% better compression. -- Synopsis // Define `QOI_IMPLEMENTATION` in *one* C/C++ file before including this // library to create the implementation. #define QOI_IMPLEMENTATION #include "qoi.h" // Encode and store an RGBA buffer to the file system. The qoi_desc describes // the input pixel data. qoi_write("image_new.qoi", rgba_pixels, &(qoi_desc){ .width = 1920, .height = 1080, .channels = 4, .colorspace = QOI_SRGB }); // Load and decode a QOI image from the file system into a 32bbp RGBA buffer. // The qoi_desc struct will be filled with the width, height, number of channels // and colorspace read from the file header. qoi_desc desc; void *rgba_pixels = qoi_read("image.qoi", &desc, 4); -- Documentation This library provides the following functions; - qoi_read -- read and decode a QOI file - qoi_decode -- decode the raw bytes of a QOI image from memory - qoi_write -- encode and write a QOI file - qoi_encode -- encode an rgba buffer into a QOI image in memory See the function declaration below for the signature and more information. If you don't want/need the qoi_read and qoi_write functions, you can define QOI_NO_STDIO before including this library. This library uses malloc() and free(). To supply your own malloc implementation you can define QOI_MALLOC and QOI_FREE before including this library. This library uses memset() to zero-initialize the index. To supply your own implementation you can define QOI_ZEROARR before including this library. -- Data Format A QOI file has a 14 byte header, followed by any number of data "chunks" and an 8-byte end marker. struct qoi_header_t { char magic[4]; // magic bytes "qoif" uint32_t width; // image width in pixels (BE) uint32_t height; // image height in pixels (BE) uint8_t channels; // 3 = RGB, 4 = RGBA uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear }; Images are encoded row by row, left to right, top to bottom. The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An image is complete when all pixels specified by width * height have been covered. Pixels are encoded as - a run of the previous pixel - an index into an array of previously seen pixels - a difference to the previous pixel value in r,g,b - full r,g,b or r,g,b,a values The color channels are assumed to not be premultiplied with the alpha channel ("un-premultiplied alpha"). A running array[64] (zero-initialized) of previously seen pixel values is maintained by the encoder and decoder. Each pixel that is seen by the encoder and decoder is put into this array at the position formed by a hash function of the color value. In the encoder, if the pixel value at the index matches the current pixel, this index position is written to the stream as QOI_OP_INDEX. The hash function for the index is: index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64 Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All values encoded in these data bits have the most significant bit on the left. The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the presence of an 8-bit tag first. The byte stream's end is marked with 7 0x00 bytes followed a single 0x01 byte. The possible chunks are: .- QOI_OP_INDEX ----------. | Byte[0] | | 7 6 5 4 3 2 1 0 | |-------+-----------------| | 0 0 | index | `-------------------------` 2-bit tag b00 6-bit index into the color index array: 0..63 A valid encoder must not issue 2 or more consecutive QOI_OP_INDEX chunks to the same index. QOI_OP_RUN should be used instead. .- QOI_OP_DIFF -----------. | Byte[0] | | 7 6 5 4 3 2 1 0 | |-------+-----+-----+-----| | 0 1 | dr | dg | db | `-------------------------` 2-bit tag b01 2-bit red channel difference from the previous pixel between -2..1 2-bit green channel difference from the previous pixel between -2..1 2-bit blue channel difference from the previous pixel between -2..1 The difference to the current channel values are using a wraparound operation, so "1 - 2" will result in 255, while "255 + 1" will result in 0. Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as 0 (b00). 1 is stored as 3 (b11). The alpha value remains unchanged from the previous pixel. .- QOI_OP_LUMA -------------------------------------. | Byte[0] | Byte[1] | | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | |-------+-----------------+-------------+-----------| | 1 0 | green diff | dr - dg | db - dg | `---------------------------------------------------` 2-bit tag b10 6-bit green channel difference from the previous pixel -32..31 4-bit red channel difference minus green channel difference -8..7 4-bit blue channel difference minus green channel difference -8..7 The green channel is used to indicate the general direction of change and is encoded in 6 bits. The red and blue channels (dr and db) base their diffs off of the green channel difference and are encoded in 4 bits. I.e.: dr_dg = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g) db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_px.g) The difference to the current channel values are using a wraparound operation, so "10 - 13" will result in 253, while "250 + 7" will result in 1. Values are stored as unsigned integers with a bias of 32 for the green channel and a bias of 8 for the red and blue channel. The alpha value remains unchanged from the previous pixel. .- QOI_OP_RUN ------------. | Byte[0] | | 7 6 5 4 3 2 1 0 | |-------+-----------------| | 1 1 | run | `-------------------------` 2-bit tag b11 6-bit run-length repeating the previous pixel: 1..62 The run-length is stored with a bias of -1. Note that the run-lengths 63 and 64 (b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and QOI_OP_RGBA tags. .- QOI_OP_RGB ------------------------------------------. | Byte[0] | Byte[1] | Byte[2] | Byte[3] | | 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | |-------------------------+---------+---------+---------| | 1 1 1 1 1 1 1 0 | red | green | blue | `-------------------------------------------------------` 8-bit tag b11111110 8-bit red channel value 8-bit green channel value 8-bit blue channel value The alpha value remains unchanged from the previous pixel. .- QOI_OP_RGBA ---------------------------------------------------. | Byte[0] | Byte[1] | Byte[2] | Byte[3] | Byte[4] | | 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | |-------------------------+---------+---------+---------+---------| | 1 1 1 1 1 1 1 1 | red | green | blue | alpha | `-----------------------------------------------------------------` 8-bit tag b11111111 8-bit red channel value 8-bit green channel value 8-bit blue channel value 8-bit alpha channel value */ /* ----------------------------------------------------------------------------- Header - Public functions */ #ifndef QOI_H #define QOI_H #ifdef __cplusplus extern "C" { #endif /* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions. It describes either the input format (for qoi_write and qoi_encode), or is filled with the description read from the file header (for qoi_read and qoi_decode). The colorspace in this qoi_desc is an enum where 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel 1 = all channels are linear You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely informative. It will be saved to the file header, but does not affect how chunks are en-/decoded. */ #define QOI_SRGB 0 #define QOI_LINEAR 1 typedef struct { unsigned int width; unsigned int height; unsigned char channels; unsigned char colorspace; } qoi_desc; #ifndef QOI_NO_STDIO /* Encode raw RGB or RGBA pixels into a QOI image and write it to the file system. The qoi_desc struct must be filled with the image width, height, number of channels (3 = RGB, 4 = RGBA) and the colorspace. The function returns 0 on failure (invalid parameters, or fopen or malloc failed) or the number of bytes written on success. */ int qoi_write(const char *filename, const void *data, const qoi_desc *desc); /* Read and decode a QOI image from the file system. If channels is 0, the number of channels from the file header is used. If channels is 3 or 4 the output format will be forced into this number of channels. The function either returns NULL on failure (invalid data, or malloc or fopen failed) or a pointer to the decoded pixels. On success, the qoi_desc struct will be filled with the description from the file header. The returned pixel data should be free()d after use. */ void *qoi_read(const char *filename, qoi_desc *desc, int channels); #endif /* QOI_NO_STDIO */ /* Encode raw RGB or RGBA pixels into a QOI image in memory. The function either returns NULL on failure (invalid parameters or malloc failed) or a pointer to the encoded data on success. On success the out_len is set to the size in bytes of the encoded data. The returned qoi data should be free()d after use. */ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len); /* Decode a QOI image from memory. The function either returns NULL on failure (invalid parameters or malloc failed) or a pointer to the decoded pixels. On success, the qoi_desc struct is filled with the description from the file header. The returned pixel data should be free()d after use. */ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels); #ifdef __cplusplus } #endif #endif /* QOI_H */ /* ----------------------------------------------------------------------------- Implementation */ #ifdef QOI_IMPLEMENTATION #include #include #ifndef QOI_MALLOC #define QOI_MALLOC(sz) malloc(sz) #define QOI_FREE(p) free(p) #endif #ifndef QOI_ZEROARR #define QOI_ZEROARR(a) memset((a),0,sizeof(a)) #endif #define QOI_OP_INDEX 0x00 /* 00xxxxxx */ #define QOI_OP_DIFF 0x40 /* 01xxxxxx */ #define QOI_OP_LUMA 0x80 /* 10xxxxxx */ #define QOI_OP_RUN 0xc0 /* 11xxxxxx */ #define QOI_OP_RGB 0xfe /* 11111110 */ #define QOI_OP_RGBA 0xff /* 11111111 */ #define QOI_MASK_2 0xc0 /* 11000000 */ #define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11) #define QOI_MAGIC \ (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \ ((unsigned int)'i') << 8 | ((unsigned int)'f')) #define QOI_HEADER_SIZE 14 /* 2GB is the max file size that this implementation can safely handle. We guard against anything larger than that, assuming the worst case with 5 bytes per pixel, rounded down to a nice clean value. 400 million pixels ought to be enough for anybody. */ #define QOI_PIXELS_MAX ((unsigned int)400000000) typedef union { struct { unsigned char r, g, b, a; } rgba; unsigned int v; } qoi_rgba_t; static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1}; static void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) { bytes[(*p)++] = (0xff000000 & v) >> 24; bytes[(*p)++] = (0x00ff0000 & v) >> 16; bytes[(*p)++] = (0x0000ff00 & v) >> 8; bytes[(*p)++] = (0x000000ff & v); } static unsigned int qoi_read_32(const unsigned char *bytes, int *p) { unsigned int a = bytes[(*p)++]; unsigned int b = bytes[(*p)++]; unsigned int c = bytes[(*p)++]; unsigned int d = bytes[(*p)++]; return a << 24 | b << 16 | c << 8 | d; } void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) { int i, max_size, p, run; int px_len, px_end, px_pos, channels; unsigned char *bytes; const unsigned char *pixels; qoi_rgba_t index[64]; qoi_rgba_t px, px_prev; if ( data == NULL || out_len == NULL || desc == NULL || desc->width == 0 || desc->height == 0 || desc->channels < 3 || desc->channels > 4 || desc->colorspace > 1 || desc->height >= QOI_PIXELS_MAX / desc->width ) { return NULL; } max_size = desc->width * desc->height * (desc->channels + 1) + QOI_HEADER_SIZE + sizeof(qoi_padding); p = 0; bytes = (unsigned char *) QOI_MALLOC(max_size); if (!bytes) { return NULL; } qoi_write_32(bytes, &p, QOI_MAGIC); qoi_write_32(bytes, &p, desc->width); qoi_write_32(bytes, &p, desc->height); bytes[p++] = desc->channels; bytes[p++] = desc->colorspace; pixels = (const unsigned char *)data; QOI_ZEROARR(index); run = 0; px_prev.rgba.r = 0; px_prev.rgba.g = 0; px_prev.rgba.b = 0; px_prev.rgba.a = 255; px = px_prev; px_len = desc->width * desc->height * desc->channels; px_end = px_len - desc->channels; channels = desc->channels; for (px_pos = 0; px_pos < px_len; px_pos += channels) { px.rgba.r = pixels[px_pos + 0]; px.rgba.g = pixels[px_pos + 1]; px.rgba.b = pixels[px_pos + 2]; if (channels == 4) { px.rgba.a = pixels[px_pos + 3]; } if (px.v == px_prev.v) { run++; if (run == 62 || px_pos == px_end) { bytes[p++] = QOI_OP_RUN | (run - 1); run = 0; } } else { int index_pos; if (run > 0) { bytes[p++] = QOI_OP_RUN | (run - 1); run = 0; } index_pos = QOI_COLOR_HASH(px) % 64; if (index[index_pos].v == px.v) { bytes[p++] = QOI_OP_INDEX | index_pos; } else { index[index_pos] = px; if (px.rgba.a == px_prev.rgba.a) { signed char vr = px.rgba.r - px_prev.rgba.r; signed char vg = px.rgba.g - px_prev.rgba.g; signed char vb = px.rgba.b - px_prev.rgba.b; signed char vg_r = vr - vg; signed char vg_b = vb - vg; if ( vr > -3 && vr < 2 && vg > -3 && vg < 2 && vb > -3 && vb < 2 ) { bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2); } else if ( vg_r > -9 && vg_r < 8 && vg > -33 && vg < 32 && vg_b > -9 && vg_b < 8 ) { bytes[p++] = QOI_OP_LUMA | (vg + 32); bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8); } else { bytes[p++] = QOI_OP_RGB; bytes[p++] = px.rgba.r; bytes[p++] = px.rgba.g; bytes[p++] = px.rgba.b; } } else { bytes[p++] = QOI_OP_RGBA; bytes[p++] = px.rgba.r; bytes[p++] = px.rgba.g; bytes[p++] = px.rgba.b; bytes[p++] = px.rgba.a; } } } px_prev = px; } for (i = 0; i < (int)sizeof(qoi_padding); i++) { bytes[p++] = qoi_padding[i]; } *out_len = p; return bytes; } void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { const unsigned char *bytes; unsigned int header_magic; unsigned char *pixels; qoi_rgba_t index[64]; qoi_rgba_t px; int px_len, chunks_len, px_pos; int p = 0, run = 0; if ( data == NULL || desc == NULL || (channels != 0 && channels != 3 && channels != 4) || size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding) ) { return NULL; } bytes = (const unsigned char *)data; header_magic = qoi_read_32(bytes, &p); desc->width = qoi_read_32(bytes, &p); desc->height = qoi_read_32(bytes, &p); desc->channels = bytes[p++]; desc->colorspace = bytes[p++]; if ( desc->width == 0 || desc->height == 0 || desc->channels < 3 || desc->channels > 4 || desc->colorspace > 1 || header_magic != QOI_MAGIC || desc->height >= QOI_PIXELS_MAX / desc->width ) { return NULL; } if (channels == 0) { channels = desc->channels; } px_len = desc->width * desc->height * channels; pixels = (unsigned char *) QOI_MALLOC(px_len); if (!pixels) { return NULL; } QOI_ZEROARR(index); px.rgba.r = 0; px.rgba.g = 0; px.rgba.b = 0; px.rgba.a = 255; chunks_len = size - (int)sizeof(qoi_padding); for (px_pos = 0; px_pos < px_len; px_pos += channels) { if (run > 0) { run--; } else if (p < chunks_len) { int b1 = bytes[p++]; if (b1 == QOI_OP_RGB) { px.rgba.r = bytes[p++]; px.rgba.g = bytes[p++]; px.rgba.b = bytes[p++]; } else if (b1 == QOI_OP_RGBA) { px.rgba.r = bytes[p++]; px.rgba.g = bytes[p++]; px.rgba.b = bytes[p++]; px.rgba.a = bytes[p++]; } else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) { px = index[b1]; } else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) { px.rgba.r += ((b1 >> 4) & 0x03) - 2; px.rgba.g += ((b1 >> 2) & 0x03) - 2; px.rgba.b += ( b1 & 0x03) - 2; } else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) { int b2 = bytes[p++]; int vg = (b1 & 0x3f) - 32; px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f); px.rgba.g += vg; px.rgba.b += vg - 8 + (b2 & 0x0f); } else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) { run = (b1 & 0x3f); } index[QOI_COLOR_HASH(px) % 64] = px; } pixels[px_pos + 0] = px.rgba.r; pixels[px_pos + 1] = px.rgba.g; pixels[px_pos + 2] = px.rgba.b; if (channels == 4) { pixels[px_pos + 3] = px.rgba.a; } } return pixels; } #ifndef QOI_NO_STDIO #include int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { FILE *f = fopen(filename, "wb"); int size; void *encoded; if (!f) { return 0; } encoded = qoi_encode(data, desc, &size); if (!encoded) { fclose(f); return 0; } fwrite(encoded, 1, size, f); fclose(f); QOI_FREE(encoded); return size; } void *qoi_read(const char *filename, qoi_desc *desc, int channels) { FILE *f = fopen(filename, "rb"); int size, bytes_read; void *pixels, *data; if (!f) { return NULL; } fseek(f, 0, SEEK_END); size = ftell(f); if (size <= 0) { fclose(f); return NULL; } fseek(f, 0, SEEK_SET); data = QOI_MALLOC(size); if (!data) { fclose(f); return NULL; } bytes_read = fread(data, 1, size, f); fclose(f); pixels = qoi_decode(data, bytes_read, desc, channels); QOI_FREE(data); return pixels; } #endif /* QOI_NO_STDIO */ #endif /* QOI_IMPLEMENTATION */ SDL2_image-2.8.8/src/stb_image.h0000664000000000000000000106454114757366445013261 0ustar00/* stb_image - v2.30 - 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.30 (2024-05-31) avoid erroneous gcc warning 2.29 (2023-05-xx) optimizations 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff 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 Katelyn Gadd (indexed color loading) 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 Neil Bickford 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 }; #if 0 /* SDL_image change */ #include typedef unsigned char stbi_uc; typedef unsigned short stbi_us; #else typedef Uint8 stbi_uc; typedef Uint16 stbi_us; #endif #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 // #if 0 /* not used in SDL_image */ STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); #endif 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 //////////////////////////////////// // // 8-bits-per-channel indexed color // Will fail if image is not an 8-bit PNG or TGA with a palette. // Palette buffer needs to be at least 256 entries for PNG. // #if 0 /* not used in SDL_image */ STBIDEF stbi_uc *stbi_load_from_memory_with_palette (stbi_uc const *buffer, int len , int *x, int *y, unsigned int *palette_buffer, int palette_buffer_len); #endif STBIDEF stbi_uc *stbi_load_from_callbacks_with_palette(stbi_io_callbacks const *clbk, void *user, int *x, int *y, unsigned int *palette_buffer, int palette_buffer_len); //////////////////////////////////// // // 16-bits-per-channel interface // #if 0 /* not used in SDL_image */ 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); #endif #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 #if 0 /* not used in SDL_image */ // 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); #endif #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 #if 0 /* SDL_image change */ // 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); #endif /**/ // free the loaded image -- this is just free() STBIDEF void stbi_image_free (void *retval_from_stbi_load); #if 0 /* not used in SDL_image */ // 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); #endif /**/ #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 #if 0 /* not used in SDL_image */ // 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); #endif /**/ #ifndef STBI_NO_THREAD_LOCALS /**/ // 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); #endif // ZLIB client - used by PNG, available for other purposes #if 0 /* not used in SDL_image */ 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); #endif /**/ #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 #if 0 /* SDL_image change */ #include #include // ptrdiff_t on osx #include #include #include #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) #include // ldexp, pow #endif #else /* SDL_image change */ #ifndef UINT_MAX #define UINT_MAX SDL_MAX_UINT32 #endif #ifndef INT_MAX #define INT_MAX SDL_MAX_SINT32 #endif #ifndef INT_MIN #define INT_MIN SDL_MIN_SINT32 #endif #ifndef SHRT_MAX #define SHRT_MAX SDL_MAX_SINT16 #endif #ifndef SHRT_MIN #define SHRT_MIN SDL_MIN_SINT16 #endif #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 #if 0 /* SDL_image change */ #if defined(_MSC_VER) || defined(__SYMBIAN32__) 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 #else typedef Uint16 stbi__uint16; typedef Sint16 stbi__int16; typedef Uint32 stbi__uint32; typedef Sint32 stbi__int32; #endif #ifndef STBI_BUFFER_SIZE #define STBI_BUFFER_SIZE 128 #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); #if 0 /* not used in SDL_image */ // 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; } #endif // 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); #if 0 /* not used in SDL_image */ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); #endif #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, unsigned int *palette_buffer, int palette_buffer_len, stbi__result_info *ri); #if 0 /* not used in SDL_image */ static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__png_is16(stbi__context *s); #endif #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, unsigned int *palette_buffer, int palette_buffer_len, 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 #ifndef STBI_NO_FAILURE_STRINGS #if 1 /* SDL_image change: */ static int stbi__err(const char *str) { IMG_SetError("%s", str); return 0; } #else /* SDL_image change. */ 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; } static int stbi__err(const char *str) { stbi__g_failure_reason = str; return 0; } #endif /**/ #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 // returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. static int stbi__addints_valid(int a, int b) { if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. return a <= INT_MAX - b; } // returns 1 if the product of two ints fits in a signed short, 0 on overflow. static int stbi__mul2shorts_valid(int a, int b) { if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN return a >= SHRT_MIN / b; } // 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; #if 0 /* not used in SDL_image */ 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; } #endif #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, unsigned int *palette_buffer, int palette_buffer_len) { 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, palette_buffer, palette_buffer_len, 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, palette_buffer, palette_buffer_len, 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; } #if 0 /* not used in SDL_image */ 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; } #endif 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_indexed(stbi__context *s, int *x, int *y, unsigned int *palette_buffer, int palette_buffer_len) { stbi__result_info ri; int comp; void *result; if (!palette_buffer) return NULL; result = stbi__load_main(s, x, y, &comp, 1, &ri, 8, palette_buffer, palette_buffer_len); if (result == NULL) return NULL; if (comp != 1) { stbi_image_free(result); return NULL; } if (ri.bits_per_channel != 8) { stbi_image_free(result); return NULL; } // @TODO: move stbi__convert_format to here if (stbi__vertically_flip_on_load) { int channels = 1; stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); } return (unsigned char *) result; } 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, NULL, 0); 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; } #if 0 /* not used in SDL_image */ 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, NULL, 0); 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; } #endif /**/ #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 #if 0 /* not used in SDL_image */ 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); } #endif /**/ 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); } #if 0 /* not used in SDL_image */ STBIDEF stbi_uc *stbi_load_from_memory_with_palette(stbi_uc const *buffer, int len, int *x, int *y, unsigned int *palette_buffer, int palette_buffer_len) { stbi__context s; stbi__start_mem(&s, buffer, len); return stbi__load_indexed(&s, x, y, palette_buffer, palette_buffer_len); } #endif STBIDEF stbi_uc *stbi_load_from_callbacks_with_palette(stbi_io_callbacks const *clbk, void *user, int *x, int *y, unsigned int *palette_buffer, int palette_buffer_len) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); return stbi__load_indexed(&s, x, y, palette_buffer, palette_buffer_len); } #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 #if 0 /* not used in SDL_image */ // 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 } #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 #if 0 /* not used in SDL_image */ 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 } #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 #ifndef STBI_NO_HDR 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; } #endif ////////////////////////////////////////////////////////////////////////////// // // 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); if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); } } 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]; if(c < 0 || c >= 256) // symbol id out of bounds! return -1; 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); if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing 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); if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing 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); if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing 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; if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); 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 if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); 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; if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); 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 if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); 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]; } if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! 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; } static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) { // some JPEGs have junk at end, skip over it but if we find what looks // like a valid marker, resume there while (!stbi__at_eof(j->s)) { stbi_uc x = stbi__get8(j->s); while (x == 0xff) { // might be a marker if (stbi__at_eof(j->s)) return STBI__MARKER_none; x = stbi__get8(j->s); if (x != 0x00 && x != 0xff) { // not a stuffed zero or lead-in to another marker, looks // like an actual marker, return it return x; } // stuffed zero has x=0 now which ends the loop, meaning we go // back to regular scan loop. // repeated 0xff keeps trying to read the next byte of the marker. } } return STBI__MARKER_none; } // 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 ) { j->marker = stbi__skip_jpeg_junk_at_end(j); // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } m = stbi__get_marker(j); if (STBI__RESTART(m)) m = stbi__get_marker(j); } 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"); m = stbi__get_marker(j); } else { if (!stbi__process_marker(j, m)) return 1; 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 if (SDL_HasNEON()) { /* SDL_image change */ 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"); memset(j, 0, sizeof(stbi__jpeg)); 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"); memset(j, 0, sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); stbi__rewind(s); STBI_FREE(j); return r; } #if 0 /* not used in SDL_image */ 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"); memset(j, 0, sizeof(stbi__jpeg)); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); STBI_FREE(j); return result; } #endif /**/ #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; int hit_zeof_once; 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)) { if (!a->hit_zeof_once) { // This is the first time we hit eof, insert 16 extra padding btis // to allow us to keep going; if we actually consume any of them // though, that is invalid data. This is caught later. a->hit_zeof_once = 1; a->num_bits += 16; // add 16 implicit zero bits } else { // We already inserted our extra 16 padding bits and are again // out, this stream is actually prematurely terminated. return -1; } } else { 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; if (a->hit_zeof_once && a->num_bits < 16) { // The first time we hit zeof, we inserted 16 extra zero bits into our bit // buffer so the decoder can just do its speculative decoding. But if we // actually consumed any of those bits (which is the case when num_bits < 16), // the stream actually read past the end so it is malformed. return stbi__err("unexpected end","Corrupt PNG"); } return 1; } if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data 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 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data 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 (len > a->zout_end - zout) { 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; a->hit_zeof_once = 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); } #if 0 /* not used in SDL_image */ 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); } #endif /**/ STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *, int, int, int *, int); /**/ 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; } } #if 0 /* not used in SDL_image */ 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 /**/ #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 filter used for first scanline to avoid needing a dummy row of 0s STBI__F_avg_first }; static stbi_uc first_row_filter[5] = { STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub }; static int stbi__paeth(int a, int b, int c) { // This formulation looks very different from the reference in the PNG spec, but is // actually equivalent and has favorable data dependencies and admits straightforward // generation of branch-free code, which helps performance significantly. int thresh = c*3 - (a + b); int lo = a < b ? a : b; int hi = a < b ? b : a; int t0 = (hi <= thresh) ? lo : c; int t1 = (thresh <= lo) ? hi : t0; return t1; } static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; // adds an extra all-255 alpha channel // dest == src is legal // img_n must be 1 or 3 static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n) { int i; // must process data backwards since we allow dest==src if (img_n == 1) { for (i=x-1; i >= 0; --i) { dest[i*2+1] = 255; dest[i*2+0] = src[i]; } } else { STBI_ASSERT(img_n == 3); for (i=x-1; i >= 0; --i) { dest[i*4+3] = 255; dest[i*4+2] = src[i*3+2]; dest[i*4+1] = src[i*3+1]; dest[i*4+0] = src[i*3+0]; } } } // 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; stbi_uc *filter_buf; int all_ok = 1; 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"); // note: error exits here don't need to clean up a->out individually, // stbi__do_png always does on error. 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); if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG"); 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"); // Allocate two scan lines worth of filter workspace buffer. filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0); if (!filter_buf) return stbi__err("outofmem", "Out of memory"); // Filtering for low-bit-depth images if (depth < 8) { filter_bytes = 1; width = img_width_bytes; } for (j=0; j < y; ++j) { // cur/prior filter buffers alternate stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes; stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes; stbi_uc *dest = a->out + stride*j; int nk = width * filter_bytes; int filter = *raw++; // check filter type if (filter > 4) { all_ok = stbi__err("invalid filter","Corrupt PNG"); break; } // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; // perform actual filtering switch (filter) { case STBI__F_none: memcpy(cur, raw, nk); break; case STBI__F_sub: memcpy(cur, raw, filter_bytes); for (k = filter_bytes; k < nk; ++k) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; case STBI__F_up: for (k = 0; k < nk; ++k) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; case STBI__F_avg: for (k = 0; k < filter_bytes; ++k) cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); for (k = filter_bytes; k < nk; ++k) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; case STBI__F_paeth: for (k = 0; k < filter_bytes; ++k) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0) for (k = filter_bytes; k < nk; ++k) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes])); break; case STBI__F_avg_first: memcpy(cur, raw, filter_bytes); for (k = filter_bytes; k < nk; ++k) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; } raw += nk; // expand decoded bits in cur to dest, also adding an extra alpha channel if desired if (depth < 8) { stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range stbi_uc *in = cur; stbi_uc *out = dest; stbi_uc inb = 0; stbi__uint32 nsmp = x*img_n; // expand bits to bytes first if (depth == 4) { for (i=0; i < nsmp; ++i) { if ((i & 1) == 0) inb = *in++; *out++ = scale * (inb >> 4); inb <<= 4; } } else if (depth == 2) { for (i=0; i < nsmp; ++i) { if ((i & 3) == 0) inb = *in++; *out++ = scale * (inb >> 6); inb <<= 2; } } else { STBI_ASSERT(depth == 1); for (i=0; i < nsmp; ++i) { if ((i & 7) == 0) inb = *in++; *out++ = scale * (inb >> 7); inb <<= 1; } } // insert alpha=255 values if desired if (img_n != out_n) stbi__create_png_alpha_expand8(dest, dest, x, img_n); } else if (depth == 8) { if (img_n == out_n) memcpy(dest, cur, x*img_n); else stbi__create_png_alpha_expand8(dest, cur, x, img_n); } else if (depth == 16) { // convert the image data from big-endian to platform-native stbi__uint16 *dest16 = (stbi__uint16*)dest; stbi__uint32 nsmp = x*img_n; if (img_n == out_n) { for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) *dest16 = (cur[0] << 8) | cur[1]; } else { STBI_ASSERT(img_n+1 == out_n); if (img_n == 1) { for (i = 0; i < x; ++i, dest16 += 2, cur += 2) { dest16[0] = (cur[0] << 8) | cur[1]; dest16[1] = 0xffff; } } else { STBI_ASSERT(img_n == 3); for (i = 0; i < x; ++i, dest16 += 4, cur += 6) { dest16[0] = (cur[0] << 8) | cur[1]; dest16[1] = (cur[2] << 8) | cur[3]; dest16[2] = (cur[4] << 8) | cur[5]; dest16[3] = 0xffff; } } } } } STBI_FREE(filter_buf); if (!all_ok) return 0; 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; #if 0 /* not used in SDL_image */ 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; } #endif #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_set_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, unsigned int *palette_buffer, int palette_buffer_len) { stbi_uc _palette[1024], pal_img_n=0; stbi_uc *palette = _palette; 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; if (palette_buffer) { if (palette_buffer_len < 256) return stbi__err("palette buffer too small", "palette buffer len must be 256"); else if (req_comp != 1) return stbi__err("invalid req_comp", "req_comp must be 1 when loading paletted"); else palette = (stbi_uc *)(void *)palette_buffer; } 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"); } 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"); } // even with 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; // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } if (z->depth == 16) { for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is } else { for (k = 0; k < s->img_n && k < 3; ++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) { // header scan definitely stops at first IDAT if (pal_img_n) s->img_n = pal_img_n; return 1; } if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); 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 (!palette_buffer) 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); if (s->io.skip && s->img_buffer_end > s->img_buffer) { // rewind the additional bytes that have been read to the buffer (s->io.skip)(s->io_user_data, (int)(s->img_buffer - s->img_buffer_end)); } 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); (void)invalid_chunk; #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, unsigned int *palette_buffer, int palette_buffer_len, stbi__result_info *ri) { void *result=NULL; if (palette_buffer && req_comp != 1) { stbi__err("bad req_comp", "req_comp must be 1 if loading paletted image without expansion"); return NULL; } if (req_comp < 0 || req_comp > 4) { stbi__err("bad req_comp", "Internal error"); return NULL; } if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp, palette_buffer, palette_buffer_len)) { 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 (palette_buffer) ; else 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) { if (palette_buffer) *n = 1; else *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, unsigned int *palette_buffer, int palette_buffer_len, stbi__result_info *ri) { stbi__png p; p.s = s; return stbi__do_png(&p, x,y,comp,req_comp, palette_buffer, palette_buffer_len, ri); } static int stbi__png_test(stbi__context *s) { int r; r = stbi__check_png_header(s); stbi__rewind(s); return r; } #if 0 /* not used in SDL_image */ static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) { if (!stbi__parse_png_file(p, STBI__SCAN_header, NULL, 0, NULL)) { 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 /**/ #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) { // accept some number of extra bytes after the header, but if the offset points either to before // the header ends or implies a large amount of extra data, reject the file as malformed int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); int header_limit = 1024; // max we actually read is below 256 bytes currently. int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { return stbi__errpuc("bad header", "Corrupt BMP"); } // we established that bytes_read_so_far is positive and sensible. // the first half of this test rejects offsets that are either too small positives, or // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn // ensures the number computed in the second half of the test can't overflow. if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { return stbi__errpuc("bad offset", "Corrupt BMP"); } else { stbi__skip(s, info.offset - bytes_read_so_far); } } 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, unsigned int *palette_buffer, int palette_buffer_len, 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 if (palette_buffer) { if (palette_buffer_len < tga_palette_len * tga_comp) { STBI_FREE(tga_data); return stbi__errpuc("buffer too small", "Palette buffer too small"); } tga_palette = (unsigned char*)(void*)palette_buffer; } else { 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); if (!palette_buffer) 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 && !palette_buffer ) { // 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 && !palette_buffer ) { 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); return 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 == 0) || (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 == 0) || (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"); if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { STBI_FREE(out); return stbi__errpuc("bad PNM", "PNM file truncated"); } if (req_comp && req_comp != s->img_n) { if (ri->bits_per_channel == 16) { out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); } else { 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); if((value > 214748364) || (value == 214748364 && *c > '7')) return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); } 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 if(*x == 0) return stbi__err("invalid width", "PPM image header had zero or overflowing width"); stbi__pnm_skip_whitespace(s, &c); *y = stbi__pnm_getinteger(s, &c); // read height if (*y == 0) return stbi__err("invalid width", "PPM image header had zero or overflowing width"); 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 #if 0 /* not used in SDL_image */ 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; } #endif /**/ #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 #if 0 /* not used in SDL_image */ 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 /**/ #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. ------------------------------------------------------------------------------ */ SDL2_image-2.8.8/src/tiny_jpeg.h0000664000000000000000000012147314444663624013304 0ustar00/** * tiny_jpeg.h * * Tiny JPEG Encoder * - Sergio Gonzalez * * This is a readable and simple single-header JPEG encoder. * * Features * - Implements Baseline DCT JPEG compression. * - No dynamic allocations. * * This library is coded in the spirit of the stb libraries and mostly follows * the stb guidelines. * * It is written in C99. And depends on the C standard library. * Works with C++11 * * * ==== Thanks ==== * * AssociationSirius (Bug reports) * Bernard van Gastel (Thread-safe defaults, BSD compilation) * * * ==== License ==== * * This software is in the public domain. Where that dedication is not * recognized, you are granted a perpetual, irrevocable license to copy and * modify this file as you see fit. * */ // ============================================================ // Usage // ============================================================ // Include "tiny_jpeg.h" to and use the public interface defined below. // // You *must* do: // // #define TJE_IMPLEMENTATION // #include "tiny_jpeg.h" // // in exactly one of your C files to actually compile the implementation. // Here is an example program that loads a bmp with stb_image and writes it // with Tiny JPEG /* #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define TJE_IMPLEMENTATION #include "tiny_jpeg.h" int main() { int width, height, num_components; unsigned char* data = stbi_load("in.bmp", &width, &height, &num_components, 0); if ( !data ) { puts("Could not find file"); return EXIT_FAILURE; } if ( !tje_encode_to_file("out.jpg", width, height, num_components, data, width * num_components) ) { fprintf(stderr, "Could not write JPEG\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } */ #ifdef __cplusplus extern "C" { #endif // ============================================================ // Public interface: // ============================================================ #ifndef TJE_HEADER_GUARD #define TJE_HEADER_GUARD // - tje_encode_to_file - // // Usage: // Takes bitmap data and writes a JPEG-encoded image to disk. // // PARAMETERS // dest_path: filename to which we will write. e.g. "out.jpg" // width, height: image size in pixels // num_components: 3 is RGB. 4 is RGBA. Those are the only supported values // src_data: pointer to the pixel data. // // RETURN: // 0 on error. 1 on success. int tje_encode_to_file(const char* dest_path, const int width, const int height, const int num_components, const unsigned char* src_data, const int pitch); // - tje_encode_to_file_at_quality - // // Usage: // Takes bitmap data and writes a JPEG-encoded image to disk. // // PARAMETERS // dest_path: filename to which we will write. e.g. "out.jpg" // quality: 3: Highest. Compression varies wildly (between 1/3 and 1/20). // 2: Very good quality. About 1/2 the size of 3. // 1: Noticeable. About 1/6 the size of 3, or 1/3 the size of 2. // width, height: image size in pixels // num_components: 3 is RGB. 4 is RGBA. Those are the only supported values // src_data: pointer to the pixel data. // // RETURN: // 0 on error. 1 on success. int tje_encode_to_file_at_quality(const char* dest_path, const int quality, const int width, const int height, const int num_components, const unsigned char* src_data, const int pitch); // - tje_encode_with_func - // // Usage // Same as tje_encode_to_file_at_quality, but it takes a callback that knows // how to handle (or ignore) `context`. The callback receives an array `data` // of `size` bytes, which can be written directly to a file. There is no need // to free the data. typedef void tje_write_func(void* context, void* data, int size); int tje_encode_with_func(tje_write_func* func, void* context, const int quality, const int width, const int height, const int num_components, const unsigned char* src_data, const int pitch); #endif // TJE_HEADER_GUARD // Implementation: In exactly one of the source files of your application, // define TJE_IMPLEMENTATION and include tiny_jpeg.h // ============================================================ // Internal // ============================================================ #ifdef TJE_IMPLEMENTATION #define tjei_min(a, b) ((a) < b) ? (a) : (b); #define tjei_max(a, b) ((a) < b) ? (b) : (a); #if defined(_MSC_VER) #define TJEI_FORCE_INLINE static __forceinline // #define TJEI_FORCE_INLINE __declspec(noinline) // For profiling #elif ((defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__)) #define TJEI_FORCE_INLINE __attribute__((always_inline)) static __inline #else #define TJEI_FORCE_INLINE static __inline #endif // Only use zero for debugging and/or inspection. #define TJE_USE_FAST_DCT 1 #if 0 /* SDL_image change */ // C std lib #include #include #include // floorf, ceilf #include // FILE, puts #include // memcpy #endif #define TJEI_BUFFER_SIZE 1024 #if 0 /* SDL_image change */ #ifdef _WIN32 #include #ifndef snprintf #define snprintf sprintf_s #endif // Not quite the same but it works for us. If I am not mistaken, it differs // only in the return value. #endif #ifndef NDEBUG #ifdef _WIN32 #define tje_log(msg) OutputDebugStringA(msg) #elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define tje_log(msg) puts(msg) #else #warning "need a tje_log definition for your platform for debugging purposes (not needed if compiling with NDEBUG)" #endif #else // NDEBUG #define tje_log(msg) #endif // NDEBUG #endif typedef struct { void* context; tje_write_func* func; } TJEWriteContext; typedef struct { // Huffman data. uint8_t ehuffsize[4][257]; uint16_t ehuffcode[4][256]; uint8_t const * ht_bits[4]; uint8_t const * ht_vals[4]; // Cuantization tables. uint8_t qt_luma[64]; uint8_t qt_chroma[64]; // fwrite by default. User-defined when using tje_encode_with_func. TJEWriteContext write_context; // Buffered output. Big performance win when using the usual stdlib implementations. size_t output_buffer_count; uint8_t output_buffer[TJEI_BUFFER_SIZE]; } TJEState; // ============================================================ // Table definitions. // // The spec defines tjei_default reasonably good quantization matrices and huffman // specification tables. // // // Instead of hard-coding the final huffman table, we only hard-code the table // spec suggested by the specification, and then derive the full table from // there. This is only for didactic purposes but it might be useful if there // ever is the case that we need to swap huffman tables from various sources. // ============================================================ // K.1 - suggested luminance QT static const uint8_t tjei_default_qt_luma_from_spec[] = { 16,11,10,16, 24, 40, 51, 61, 12,12,14,19, 26, 58, 60, 55, 14,13,16,24, 40, 57, 69, 56, 14,17,22,29, 51, 87, 80, 62, 18,22,37,56, 68,109,103, 77, 24,35,55,64, 81,104,113, 92, 49,64,78,87,103,121,120,101, 72,92,95,98,112,100,103, 99, }; // Unused #if 0 static const uint8_t tjei_default_qt_chroma_from_spec[] = { // K.1 - suggested chrominance QT 17,18,24,47,99,99,99,99, 18,21,26,66,99,99,99,99, 24,26,56,99,99,99,99,99, 47,66,99,99,99,99,99,99, 99,99,99,99,99,99,99,99, 99,99,99,99,99,99,99,99, 99,99,99,99,99,99,99,99, 99,99,99,99,99,99,99,99, }; #endif static const uint8_t tjei_default_qt_chroma_from_paper[] = { // Example QT from JPEG paper 16, 12, 14, 14, 18, 24, 49, 72, 11, 10, 16, 24, 40, 51, 61, 12, 13, 17, 22, 35, 64, 92, 14, 16, 22, 37, 55, 78, 95, 19, 24, 29, 56, 64, 87, 98, 26, 40, 51, 68, 81, 103, 112, 58, 57, 87, 109, 104, 121,100, 60, 69, 80, 103, 113, 120, 103, 55, 56, 62, 77, 92, 101, 99, }; // == Procedure to 'deflate' the huffman tree: JPEG spec, C.2 // Number of 16 bit values for every code length. (K.3.3.1) static const uint8_t tjei_default_ht_luma_dc_len[16] = { 0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0 }; // values static const uint8_t tjei_default_ht_luma_dc[12] = { 0,1,2,3,4,5,6,7,8,9,10,11 }; // Number of 16 bit values for every code length. (K.3.3.1) static const uint8_t tjei_default_ht_chroma_dc_len[16] = { 0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0 }; // values static const uint8_t tjei_default_ht_chroma_dc[12] = { 0,1,2,3,4,5,6,7,8,9,10,11 }; // Same as above, but AC coefficients. static const uint8_t tjei_default_ht_luma_ac_len[16] = { 0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d }; static const uint8_t tjei_default_ht_luma_ac[] = { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA }; static const uint8_t tjei_default_ht_chroma_ac_len[16] = { 0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77 }; static const uint8_t tjei_default_ht_chroma_ac[] = { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA }; // ============================================================ // Code // ============================================================ // Zig-zag order: static const uint8_t tjei_zig_zag[64] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63, }; // Memory order as big endian. // On little-endian machines: 0xhilo -> 0xlohi which looks as 0xhi 0xlo in memory // On big-endian machines: leave 0xhilo unchanged static uint16_t tjei_be_word(const uint16_t native_word) { uint8_t bytes[2]; uint16_t result; bytes[1] = (native_word & 0x00ff); bytes[0] = ((native_word & 0xff00) >> 8); memcpy(&result, bytes, sizeof(bytes)); return result; } // ============================================================ // The following structs exist only for code clarity, debugability, and // readability. They are used when writing to disk, but it is useful to have // 1-packed-structs to document how the format works, and to inspect memory // while developing. // ============================================================ static const uint8_t tjeik_jfif_id[] = "JFIF"; static const uint8_t tjeik_com_str[] = "Created by Tiny JPEG Encoder"; // TODO: Get rid of packed structs! #pragma pack(push,1) typedef struct { uint16_t SOI; // JFIF header. uint16_t APP0; uint16_t jfif_len; uint8_t jfif_id[5]; uint16_t version; uint8_t units; uint16_t x_density; uint16_t y_density; uint8_t x_thumb; uint8_t y_thumb; } TJEJPEGHeader; typedef struct { uint16_t com; uint16_t com_len; char com_str[sizeof(tjeik_com_str) - 1]; } TJEJPEGComment; // Helper struct for TJEFrameHeader (below). typedef struct { uint8_t component_id; uint8_t sampling_factors; // most significant 4 bits: horizontal. 4 LSB: vertical (A.1.1) uint8_t qt; // Quantization table selector. } TJEComponentSpec; typedef struct { uint16_t SOF; uint16_t len; // 8 + 3 * frame.num_components uint8_t precision; // Sample precision (bits per sample). uint16_t height; uint16_t width; uint8_t num_components; // For this implementation, will be equal to 3. TJEComponentSpec component_spec[3]; } TJEFrameHeader; typedef struct { uint8_t component_id; // Just as with TJEComponentSpec uint8_t dc_ac; // (dc|ac) } TJEFrameComponentSpec; typedef struct { uint16_t SOS; uint16_t len; uint8_t num_components; // 3. TJEFrameComponentSpec component_spec[3]; uint8_t first; // 0 uint8_t last; // 63 uint8_t ah_al; // o } TJEScanHeader; #pragma pack(pop) static void tjei_write(TJEState* state, const void* data, size_t num_bytes, size_t num_elements) { size_t to_write = num_bytes * num_elements; // Cap to the buffer available size and copy memory. size_t capped_count = tjei_min(to_write, TJEI_BUFFER_SIZE - 1 - state->output_buffer_count); memcpy(state->output_buffer + state->output_buffer_count, data, capped_count); state->output_buffer_count += capped_count; assert (state->output_buffer_count <= TJEI_BUFFER_SIZE - 1); // Flush the buffer. if ( state->output_buffer_count == TJEI_BUFFER_SIZE - 1 ) { state->write_context.func(state->write_context.context, state->output_buffer, (int)state->output_buffer_count); state->output_buffer_count = 0; } // Recursively calling ourselves with the rest of the buffer. if (capped_count < to_write) { tjei_write(state, (uint8_t*)data+capped_count, to_write - capped_count, 1); } } static void tjei_write_DQT(TJEState* state, const uint8_t* matrix, uint8_t id) { uint16_t DQT = tjei_be_word(0xffdb); uint16_t len = tjei_be_word(0x0043); // 2(len) + 1(id) + 64(matrix) = 67 = 0x43 uint8_t precision_and_id = id; // 0x0000 8 bits | 0x00id tjei_write(state, &DQT, sizeof(uint16_t), 1); tjei_write(state, &len, sizeof(uint16_t), 1); assert(id < 4); tjei_write(state, &precision_and_id, sizeof(uint8_t), 1); // Write matrix tjei_write(state, matrix, 64*sizeof(uint8_t), 1); } typedef enum { TJEI_DC = 0, TJEI_AC = 1 } TJEHuffmanTableClass; static void tjei_write_DHT(TJEState* state, uint8_t const * matrix_len, uint8_t const * matrix_val, TJEHuffmanTableClass ht_class, uint8_t id) { int i, num_values = 0; uint16_t DHT, len; uint8_t tc_th; for ( i = 0; i < 16; ++i ) { num_values += matrix_len[i]; } assert(num_values <= 0xffff); DHT = tjei_be_word(0xffc4); // 2(len) + 1(Tc|th) + 16 (num lengths) + ?? (num values) len = tjei_be_word(2 + 1 + 16 + (uint16_t)num_values); assert(id < 4); tc_th = (uint8_t)((((uint8_t)ht_class) << 4) | id); tjei_write(state, &DHT, sizeof(uint16_t), 1); tjei_write(state, &len, sizeof(uint16_t), 1); tjei_write(state, &tc_th, sizeof(uint8_t), 1); tjei_write(state, matrix_len, sizeof(uint8_t), 16); tjei_write(state, matrix_val, sizeof(uint8_t), (size_t)num_values); } // ============================================================ // Huffman deflation code. // ============================================================ // Returns all code sizes from the BITS specification (JPEG C.3) static uint8_t* tjei_huff_get_code_lengths(uint8_t huffsize[/*256*/], uint8_t const * bits) { int i, j, k = 0; for ( i = 0; i < 16; ++i ) { for ( j = 0; j < bits[i]; ++j ) { huffsize[k++] = (uint8_t)(i + 1); } huffsize[k] = 0; } return huffsize; } // Fills out the prefixes for each code. static uint16_t* tjei_huff_get_codes(uint16_t codes[], uint8_t* huffsize, int64_t count) { uint16_t code = 0; int k = 0; uint8_t sz = huffsize[0]; for(;;) { do { assert(k < count); codes[k++] = code++; } while (huffsize[k] == sz); if (huffsize[k] == 0) { return codes; } do { code = (uint16_t)(code << 1); ++sz; } while( huffsize[k] != sz ); } } static void tjei_huff_get_extended(uint8_t* out_ehuffsize, uint16_t* out_ehuffcode, uint8_t const * huffval, uint8_t* huffsize, uint16_t* huffcode, int64_t count) { int k = 0; do { uint8_t val = huffval[k]; out_ehuffcode[val] = huffcode[k]; out_ehuffsize[val] = huffsize[k]; k++; } while ( k < count ); } // ============================================================ // Returns: // out[1] : number of bits // out[0] : bits TJEI_FORCE_INLINE void tjei_calculate_variable_length_int(int value, uint16_t out[2]) { int abs_val = value; if ( value < 0 ) { abs_val = -abs_val; --value; } out[1] = 1; while( abs_val >>= 1 ) { ++out[1]; } out[0] = (uint16_t)(value & ((1 << out[1]) - 1)); } // Write bits to file. TJEI_FORCE_INLINE void tjei_write_bits(TJEState* state, uint32_t* bitbuffer, uint32_t* location, uint16_t num_bits, uint16_t bits) { // v-- location // [ ] <-- bit buffer // 32 0 // // This call pushes to the bitbuffer and saves the location. Data is pushed // from most significant to less significant. // When we can write a full byte, we write a byte and shift. // Push the stack. uint32_t nloc = *location + num_bits; *bitbuffer |= (uint32_t)(bits << (32 - nloc)); *location = nloc; while ( *location >= 8 ) { // Grab the most significant byte. uint8_t c = (uint8_t)((*bitbuffer) >> 24); // Write it to file. tjei_write(state, &c, 1, 1); if ( c == 0xff ) { // Special case: tell JPEG this is not a marker. char z = 0; tjei_write(state, &z, 1, 1); } // Pop the stack. *bitbuffer <<= 8; *location -= 8; } } #if TJE_USE_FAST_DCT // DCT implementation by Thomas G. Lane. // Obtained through NVIDIA // http://developer.download.nvidia.com/SDK/9.5/Samples/vidimaging_samples.html#gpgpu_dct // // QUOTE: // This implementation is based on Arai, Agui, and Nakajima's algorithm for // scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in // Japanese, but the algorithm is described in the Pennebaker & Mitchell // JPEG textbook (see REFERENCES section in file README). The following code // is based directly on figure 4-8 in P&M. // static void tjei_fdct (float * data) { float tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; float tmp10, tmp11, tmp12, tmp13; float z1, z2, z3, z4, z5, z11, z13; float *dataptr; int ctr; /* Pass 1: process rows. */ dataptr = data; for ( ctr = 7; ctr >= 0; ctr-- ) { tmp0 = dataptr[0] + dataptr[7]; tmp7 = dataptr[0] - dataptr[7]; tmp1 = dataptr[1] + dataptr[6]; tmp6 = dataptr[1] - dataptr[6]; tmp2 = dataptr[2] + dataptr[5]; tmp5 = dataptr[2] - dataptr[5]; tmp3 = dataptr[3] + dataptr[4]; tmp4 = dataptr[3] - dataptr[4]; /* Even part */ tmp10 = tmp0 + tmp3; /* phase 2 */ tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp1 - tmp2; dataptr[0] = tmp10 + tmp11; /* phase 3 */ dataptr[4] = tmp10 - tmp11; z1 = (tmp12 + tmp13) * ((float) 0.707106781); /* c4 */ dataptr[2] = tmp13 + z1; /* phase 5 */ dataptr[6] = tmp13 - z1; /* Odd part */ tmp10 = tmp4 + tmp5; /* phase 2 */ tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; /* The rotator is modified from fig 4-8 to avoid extra negations. */ z5 = (tmp10 - tmp12) * ((float) 0.382683433); /* c6 */ z2 = ((float) 0.541196100) * tmp10 + z5; /* c2-c6 */ z4 = ((float) 1.306562965) * tmp12 + z5; /* c2+c6 */ z3 = tmp11 * ((float) 0.707106781); /* c4 */ z11 = tmp7 + z3; /* phase 5 */ z13 = tmp7 - z3; dataptr[5] = z13 + z2; /* phase 6 */ dataptr[3] = z13 - z2; dataptr[1] = z11 + z4; dataptr[7] = z11 - z4; dataptr += 8; /* advance pointer to next row */ } /* Pass 2: process columns. */ dataptr = data; for ( ctr = 8-1; ctr >= 0; ctr-- ) { tmp0 = dataptr[8*0] + dataptr[8*7]; tmp7 = dataptr[8*0] - dataptr[8*7]; tmp1 = dataptr[8*1] + dataptr[8*6]; tmp6 = dataptr[8*1] - dataptr[8*6]; tmp2 = dataptr[8*2] + dataptr[8*5]; tmp5 = dataptr[8*2] - dataptr[8*5]; tmp3 = dataptr[8*3] + dataptr[8*4]; tmp4 = dataptr[8*3] - dataptr[8*4]; /* Even part */ tmp10 = tmp0 + tmp3; /* phase 2 */ tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp1 - tmp2; dataptr[8*0] = tmp10 + tmp11; /* phase 3 */ dataptr[8*4] = tmp10 - tmp11; z1 = (tmp12 + tmp13) * ((float) 0.707106781); /* c4 */ dataptr[8*2] = tmp13 + z1; /* phase 5 */ dataptr[8*6] = tmp13 - z1; /* Odd part */ tmp10 = tmp4 + tmp5; /* phase 2 */ tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; /* The rotator is modified from fig 4-8 to avoid extra negations. */ z5 = (tmp10 - tmp12) * ((float) 0.382683433); /* c6 */ z2 = ((float) 0.541196100) * tmp10 + z5; /* c2-c6 */ z4 = ((float) 1.306562965) * tmp12 + z5; /* c2+c6 */ z3 = tmp11 * ((float) 0.707106781); /* c4 */ z11 = tmp7 + z3; /* phase 5 */ z13 = tmp7 - z3; dataptr[8*5] = z13 + z2; /* phase 6 */ dataptr[8*3] = z13 - z2; dataptr[8*1] = z11 + z4; dataptr[8*7] = z11 - z4; dataptr++; /* advance pointer to next column */ } } #endif #if !TJE_USE_FAST_DCT static float slow_fdct(int u, int v, float* data) { #define kPI 3.14159265f float res = 0.0f; float cu = (u == 0) ? 0.70710678118654f : 1; float cv = (v == 0) ? 0.70710678118654f : 1; int x, y; for ( y = 0; y < 8; ++y ) { for ( x = 0; x < 8; ++x ) { res += (data[y * 8 + x]) * cosf(((2.0f * x + 1.0f) * u * kPI) / 16.0f) * cosf(((2.0f * y + 1.0f) * v * kPI) / 16.0f); } } res *= 0.25f * cu * cv; return res; #undef kPI } #endif #define ABS(x) ((x) < 0 ? -(x) : (x)) static void tjei_encode_and_write_MCU(TJEState* state, float* mcu, #if TJE_USE_FAST_DCT float* qt, // Pre-processed quantization matrix. #else uint8_t* qt, #endif uint8_t* huff_dc_len, uint16_t* huff_dc_code, // Huffman tables uint8_t* huff_ac_len, uint16_t* huff_ac_code, int* pred, // Previous DC coefficient uint32_t* bitbuffer, // Bitstack. uint32_t* location) { int du[64]; // Data unit in zig-zag order float dct_mcu[64]; int i; #if !TJE_USE_FAST_DCT int u, v; #endif int last_non_zero_i; int diff; uint16_t vli[2]; memcpy(dct_mcu, mcu, 64 * sizeof(float)); #if TJE_USE_FAST_DCT tjei_fdct(dct_mcu); for ( i = 0; i < 64; ++i ) { float fval = dct_mcu[i]; int val; fval *= qt[i]; #if 0 fval = (fval > 0) ? floorf(fval + 0.5f) : ceilf(fval - 0.5f); #else fval = floorf(fval + 1024 + 0.5f); fval -= 1024; #endif val = (int)fval; du[tjei_zig_zag[i]] = val; } #else for ( v = 0; v < 8; ++v ) { for ( u = 0; u < 8; ++u ) { dct_mcu[v * 8 + u] = slow_fdct(u, v, mcu); } } for ( i = 0; i < 64; ++i ) { float fval = dct_mcu[i] / (qt[i]); int val = (int)((fval > 0) ? floorf(fval + 0.5f) : ceilf(fval - 0.5f)); du[tjei_zig_zag[i]] = val; } #endif // Encode DC coefficient. diff = du[0] - *pred; *pred = du[0]; if ( diff != 0 ) { tjei_calculate_variable_length_int(diff, vli); // Write number of bits with Huffman coding tjei_write_bits(state, bitbuffer, location, huff_dc_len[vli[1]], huff_dc_code[vli[1]]); // Write the bits. tjei_write_bits(state, bitbuffer, location, vli[1], vli[0]); } else { tjei_write_bits(state, bitbuffer, location, huff_dc_len[0], huff_dc_code[0]); } // ==== Encode AC coefficients ==== last_non_zero_i = 0; // Find the last non-zero element. for ( i = 63; i > 0; --i ) { if (du[i] != 0) { last_non_zero_i = i; break; } } for ( i = 1; i <= last_non_zero_i; ++i ) { // If zero, increase count. If >=15, encode (FF,00) int zero_count = 0; uint16_t sym1; while ( du[i] == 0 ) { ++zero_count; ++i; if (zero_count == 16) { // encode (ff,00) == 0xf0 tjei_write_bits(state, bitbuffer, location, huff_ac_len[0xf0], huff_ac_code[0xf0]); zero_count = 0; } } tjei_calculate_variable_length_int(du[i], vli); assert(zero_count < 0x10); assert(vli[1] <= 10); sym1 = (uint16_t)((uint16_t)zero_count << 4) | vli[1]; assert(huff_ac_len[sym1] != 0); // Write symbol 1 --- (RUNLENGTH, SIZE) tjei_write_bits(state, bitbuffer, location, huff_ac_len[sym1], huff_ac_code[sym1]); // Write symbol 2 --- (AMPLITUDE) tjei_write_bits(state, bitbuffer, location, vli[1], vli[0]); } if (last_non_zero_i != 63) { // write EOB HUFF(00,00) tjei_write_bits(state, bitbuffer, location, huff_ac_len[0], huff_ac_code[0]); } return; } enum { TJEI_LUMA_DC, TJEI_LUMA_AC, TJEI_CHROMA_DC, TJEI_CHROMA_AC, }; #if TJE_USE_FAST_DCT struct TJEProcessedQT { float chroma[64]; float luma[64]; }; #endif // Set up huffman tables in state. static void tjei_huff_expand(TJEState* state) { int32_t spec_tables_len[4]; uint8_t huffsize[4][257]; uint16_t huffcode[4][256]; int i, k; assert(state); memset(spec_tables_len, 0, sizeof(spec_tables_len)); state->ht_bits[TJEI_LUMA_DC] = tjei_default_ht_luma_dc_len; state->ht_bits[TJEI_LUMA_AC] = tjei_default_ht_luma_ac_len; state->ht_bits[TJEI_CHROMA_DC] = tjei_default_ht_chroma_dc_len; state->ht_bits[TJEI_CHROMA_AC] = tjei_default_ht_chroma_ac_len; state->ht_vals[TJEI_LUMA_DC] = tjei_default_ht_luma_dc; state->ht_vals[TJEI_LUMA_AC] = tjei_default_ht_luma_ac; state->ht_vals[TJEI_CHROMA_DC] = tjei_default_ht_chroma_dc; state->ht_vals[TJEI_CHROMA_AC] = tjei_default_ht_chroma_ac; // How many codes in total for each of LUMA_(DC|AC) and CHROMA_(DC|AC) for ( i = 0; i < 4; ++i ) { for ( k = 0; k < 16; ++k ) { spec_tables_len[i] += state->ht_bits[i][k]; } } // Fill out the extended tables.. for ( i = 0; i < 4; ++i ) { assert (256 >= spec_tables_len[i]); tjei_huff_get_code_lengths(huffsize[i], state->ht_bits[i]); tjei_huff_get_codes(huffcode[i], huffsize[i], spec_tables_len[i]); } for ( i = 0; i < 4; ++i ) { int64_t count = spec_tables_len[i]; tjei_huff_get_extended(state->ehuffsize[i], state->ehuffcode[i], state->ht_vals[i], &huffsize[i][0], &huffcode[i][0], count); } } static int tjei_encode_main(TJEState* state, const unsigned char* src_data, const int width, const int height, const int src_num_components, const int pitch) { #if TJE_USE_FAST_DCT // Again, taken from classic japanese implementation. // /* For float AA&N IDCT method, divisors are equal to quantization * coefficients scaled by scalefactor[row]*scalefactor[col], where * scalefactor[0] = 1 * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 * We apply a further scale factor of 8. * What's actually stored is 1/divisor so that the inner loop can * use a multiplication rather than a division. */ static const float aan_scales[] = { 1.0f, 1.387039845f, 1.306562965f, 1.175875602f, 1.0f, 0.785694958f, 0.541196100f, 0.275899379f }; struct TJEProcessedQT pqt; #endif float du_y[64]; float du_b[64]; float du_r[64]; int pred_y; int pred_b; int pred_r; int x, y, off_x, off_y; uint32_t bitbuffer; uint32_t location; uint16_t EOI; if (src_num_components != 3 && src_num_components != 4) { return 0; } if (width > 0xffff || height > 0xffff) { return 0; } #if TJE_USE_FAST_DCT // build (de)quantization tables for(y=0; y<8; y++) { for(x=0; x<8; x++) { int i = y*8 + x; pqt.luma[y*8+x] = 1.0f / (8 * aan_scales[x] * aan_scales[y] * state->qt_luma[tjei_zig_zag[i]]); pqt.chroma[y*8+x] = 1.0f / (8 * aan_scales[x] * aan_scales[y] * state->qt_chroma[tjei_zig_zag[i]]); } } #endif { // Write header TJEJPEGHeader header; uint16_t jfif_len; // JFIF header. header.SOI = tjei_be_word(0xffd8); // Sequential DCT header.APP0 = tjei_be_word(0xffe0); jfif_len = sizeof(TJEJPEGHeader) - 4 /*SOI & APP0 markers*/; header.jfif_len = tjei_be_word(jfif_len); memcpy(header.jfif_id, (void*)tjeik_jfif_id, 5); header.version = tjei_be_word(0x0102); header.units = 0x01; // Dots-per-inch header.x_density = tjei_be_word(0x0060); // 96 DPI header.y_density = tjei_be_word(0x0060); // 96 DPI header.x_thumb = 0; header.y_thumb = 0; tjei_write(state, &header, sizeof(TJEJPEGHeader), 1); } { // Write comment TJEJPEGComment com; uint16_t com_len = 2 + sizeof(tjeik_com_str) - 1; // Comment com.com = tjei_be_word(0xfffe); com.com_len = tjei_be_word(com_len); memcpy(com.com_str, (void*)tjeik_com_str, sizeof(tjeik_com_str)-1); tjei_write(state, &com, sizeof(TJEJPEGComment), 1); } // Write quantization tables. tjei_write_DQT(state, state->qt_luma, 0x00); tjei_write_DQT(state, state->qt_chroma, 0x01); { // Write the frame marker. TJEFrameHeader header; uint8_t tables[3] = { 0, // Luma component gets luma table (see tjei_write_DQT call above.) 1, // Chroma component gets chroma table 1, // Chroma component gets chroma table }; int i; header.SOF = tjei_be_word(0xffc0); header.len = tjei_be_word(8 + 3 * 3); header.precision = 8; assert(width <= 0xffff); assert(height <= 0xffff); header.width = tjei_be_word((uint16_t)width); header.height = tjei_be_word((uint16_t)height); header.num_components = 3; for (i = 0; i < 3; ++i) { TJEComponentSpec spec; spec.component_id = (uint8_t)(i + 1); // No particular reason. Just 1, 2, 3. spec.sampling_factors = (uint8_t)0x11; spec.qt = tables[i]; header.component_spec[i] = spec; } // Write to file. tjei_write(state, &header, sizeof(TJEFrameHeader), 1); } tjei_write_DHT(state, state->ht_bits[TJEI_LUMA_DC], state->ht_vals[TJEI_LUMA_DC], TJEI_DC, 0); tjei_write_DHT(state, state->ht_bits[TJEI_LUMA_AC], state->ht_vals[TJEI_LUMA_AC], TJEI_AC, 0); tjei_write_DHT(state, state->ht_bits[TJEI_CHROMA_DC], state->ht_vals[TJEI_CHROMA_DC], TJEI_DC, 1); tjei_write_DHT(state, state->ht_bits[TJEI_CHROMA_AC], state->ht_vals[TJEI_CHROMA_AC], TJEI_AC, 1); // Write start of scan { TJEScanHeader header; uint8_t tables[3] = { 0x00, 0x11, 0x11, }; int i; header.SOS = tjei_be_word(0xffda); header.len = tjei_be_word((uint16_t)(6 + (sizeof(TJEFrameComponentSpec) * 3))); header.num_components = 3; for (i = 0; i < 3; ++i) { TJEFrameComponentSpec cs; // Must be equal to component_id from frame header above. cs.component_id = (uint8_t)(i + 1); cs.dc_ac = (uint8_t)tables[i]; header.component_spec[i] = cs; } header.first = 0; header.last = 63; header.ah_al = 0; tjei_write(state, &header, sizeof(TJEScanHeader), 1); } // Write compressed data. // Set diff to 0. pred_y = 0; pred_b = 0; pred_r = 0; // Bit stack bitbuffer = 0; location = 0; for ( y = 0; y < height; y += 8 ) { for ( x = 0; x < width; x += 8 ) { // Block loop: ==== for ( off_y = 0; off_y < 8; ++off_y ) { for ( off_x = 0; off_x < 8; ++off_x ) { int block_index = (off_y * 8 + off_x); int src_index = (((y + off_y) * pitch) + ((x + off_x) * src_num_components)); int col = x + off_x; int row = y + off_y; float luma, cb, cr; uint8_t r, g, b; if(row >= height) { src_index -= (pitch * (row - height + 1)); } if(col >= width) { src_index -= (col - width + 1) * src_num_components; } assert(src_index < height * pitch); r = src_data[src_index + 0]; g = src_data[src_index + 1]; b = src_data[src_index + 2]; luma = 0.299f * r + 0.587f * g + 0.114f * b - 128; cb = -0.1687f * r - 0.3313f * g + 0.5f * b; cr = 0.5f * r - 0.4187f * g - 0.0813f * b; du_y[block_index] = luma; du_b[block_index] = cb; du_r[block_index] = cr; } } tjei_encode_and_write_MCU(state, du_y, #if TJE_USE_FAST_DCT pqt.luma, #else state->qt_luma, #endif state->ehuffsize[TJEI_LUMA_DC], state->ehuffcode[TJEI_LUMA_DC], state->ehuffsize[TJEI_LUMA_AC], state->ehuffcode[TJEI_LUMA_AC], &pred_y, &bitbuffer, &location); tjei_encode_and_write_MCU(state, du_b, #if TJE_USE_FAST_DCT pqt.chroma, #else state->qt_chroma, #endif state->ehuffsize[TJEI_CHROMA_DC], state->ehuffcode[TJEI_CHROMA_DC], state->ehuffsize[TJEI_CHROMA_AC], state->ehuffcode[TJEI_CHROMA_AC], &pred_b, &bitbuffer, &location); tjei_encode_and_write_MCU(state, du_r, #if TJE_USE_FAST_DCT pqt.chroma, #else state->qt_chroma, #endif state->ehuffsize[TJEI_CHROMA_DC], state->ehuffcode[TJEI_CHROMA_DC], state->ehuffsize[TJEI_CHROMA_AC], state->ehuffcode[TJEI_CHROMA_AC], &pred_r, &bitbuffer, &location); } } // Finish the image. { // Flush if (location > 0 && location < 8) { tjei_write_bits(state, &bitbuffer, &location, (uint16_t)(8 - location), 0); } } EOI = tjei_be_word(0xffd9); tjei_write(state, &EOI, sizeof(uint16_t), 1); if (state->output_buffer_count) { state->write_context.func(state->write_context.context, state->output_buffer, (int)state->output_buffer_count); state->output_buffer_count = 0; } return 1; } #if 0 /* SDL_image change */ int tje_encode_to_file(const char* dest_path, const int width, const int height, const int num_components, const unsigned char* src_data, const int pitch) { int res = tje_encode_to_file_at_quality(dest_path, 3, width, height, num_components, src_data, pitch); return res; } static void tjei_stdlib_func(void* context, void* data, int size) { FILE* fd = (FILE*)context; fwrite(data, size, 1, fd); } // Define public interface. int tje_encode_to_file_at_quality(const char* dest_path, const int quality, const int width, const int height, const int num_components, const unsigned char* src_data, const int pitch) { FILE* fd = fopen(dest_path, "wb"); if (!fd) { tje_log("Could not open file for writing."); return 0; } int result = tje_encode_with_func(tjei_stdlib_func, fd, quality, width, height, num_components, src_data, pitch); result |= 0 == fclose(fd); return result; } #endif int tje_encode_with_func(tje_write_func* func, void* context, const int quality, const int width, const int height, const int num_components, const unsigned char* src_data, const int pitch) { TJEState state; TJEWriteContext wc; uint8_t qt_factor; int i; if (quality < 1 || quality > 3) { tje_log("[ERROR] -- Valid 'quality' values are 1 (lowest), 2, or 3 (highest)\n"); return 0; } qt_factor = 1; memset(&state, 0, sizeof(state)); memset(&wc, 0, sizeof(wc)); switch(quality) { case 3: for ( i = 0; i < 64; ++i ) { state.qt_luma[i] = 1; state.qt_chroma[i] = 1; } break; case 2: qt_factor = 10; /* fallthrough */ case 1: for ( i = 0; i < 64; ++i ) { state.qt_luma[i] = tjei_default_qt_luma_from_spec[i] / qt_factor; if (state.qt_luma[i] == 0) { state.qt_luma[i] = 1; } state.qt_chroma[i] = tjei_default_qt_chroma_from_paper[i] / qt_factor; if (state.qt_chroma[i] == 0) { state.qt_chroma[i] = 1; } } break; default: assert(!"invalid code path"); break; } wc.context = context; wc.func = func; state.write_context = wc; tjei_huff_expand(&state); return tjei_encode_main(&state, src_data, width, height, num_components, pitch); } // ============================================================ #endif // TJE_IMPLEMENTATION // ============================================================ // #ifdef __cplusplus } // extern C #endif SDL2_image-2.8.8/src/version.rc0000664000000000000000000000172014761421756013146 0ustar00 #include "winresrc.h" LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION 2,8,8,0 PRODUCTVERSION 2,8,8,0 FILEFLAGSMASK 0x3fL FILEFLAGS 0x0L FILEOS 0x40004L FILETYPE 0x2L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "\0" VALUE "FileDescription", "SDL_image\0" VALUE "FileVersion", "2, 8, 8, 0\0" VALUE "InternalName", "SDL_image\0" VALUE "LegalCopyright", "Copyright (C) 2025 Sam Lantinga\0" VALUE "OriginalFilename", "SDL_image.dll\0" VALUE "ProductName", "Simple DirectMedia Layer\0" VALUE "ProductVersion", "2, 8, 8, 0\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END SDL2_image-2.8.8/test-driver0000775000000000000000000001141714241731060012527 0ustar00#! /bin/sh # test-driver - basic testsuite driver script. scriptversion=2018-03-07.03; # UTC # Copyright (C) 2011-2021 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # Make unconditional expansion of undefined variables an error. This # helps a lot in preventing typo-related bugs. set -u usage_error () { echo "$0: $*" >&2 print_usage >&2 exit 2 } print_usage () { cat <"$log_file" "$@" >>"$log_file" 2>&1 estatus=$? if test $enable_hard_errors = no && test $estatus -eq 99; then tweaked_estatus=1 else tweaked_estatus=$estatus fi case $tweaked_estatus:$expect_failure in 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; 0:*) col=$grn res=PASS recheck=no gcopy=no;; 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; *:*) col=$red res=FAIL recheck=yes gcopy=yes;; esac # Report the test outcome and exit status in the logs, so that one can # know whether the test passed or failed simply by looking at the '.log' # file, without the need of also peaking into the corresponding '.trs' # file (automake bug#11814). echo "$res $test_name (exit status: $estatus)" >>"$log_file" # Report outcome to console. echo "${col}${res}${std}: $test_name" # Register the test result, and other relevant metadata. echo ":test-result: $res" > $trs_file echo ":global-test-result: $res" >> $trs_file echo ":recheck: $recheck" >> $trs_file echo ":copy-in-global-log: $gcopy" >> $trs_file # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: SDL2_image-2.8.8/test/.gitignore0000664000000000000000000000007514241727434013310 0ustar00/*.test /CompareSurfaces*.bmp /save-*.bmp /save.* /testimage SDL2_image-2.8.8/test/CMakeLists.txt0000664000000000000000000000404114477134706014062 0ustar00enable_testing() if(NOT TARGET SDL2::SDL2main) find_package(SDL2main) endif() if(NOT TARGET SDL2::SDL2test) find_package(SDL2test REQUIRED) endif() add_executable(testimage main.c) set(ALL_TESTS testimage ) set(RESOURCE_FILES palette.bmp palette.gif sample.avif sample.bmp sample.cur sample.ico sample.jpg sample.jxl sample.pcx sample.png sample.pnm sample.qoi sample.tga sample.tif sample.webp sample.xcf sample.xpm svg-class.bmp svg-class.svg svg.bmp svg.svg svg64.bmp ) set(TESTS_ENVIRONMENT "SDL_TEST_SRCDIR=${CMAKE_CURRENT_SOURCE_DIR}" "SDL_TEST_BUILDDIR=${CMAKE_CURRENT_BINARY_DIR}" "SDL_VIDEODRIVER=dummy" ) foreach(prog ${ALL_TESTS}) target_compile_definitions(${prog} PRIVATE $) target_link_libraries(${prog} PRIVATE SDL2_image::${sdl2_image_export_name}) if(TARGET SDL2::SDL2main) target_link_libraries(${prog} PRIVATE SDL2::SDL2main) endif() target_link_libraries(${prog} PRIVATE SDL2::SDL2test ${sdl2_target_name}) add_test( NAME ${prog} COMMAND ${prog} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) set_tests_properties( ${prog} PROPERTIES ENVIRONMENT "${TESTS_ENVIRONMENT}" TIMEOUT 30 ) if(SDL2IMAGE_TESTS_INSTALL) set(exe ${prog}) set(installedtestsdir "${CMAKE_INSTALL_FULL_LIBEXECDIR}/installed-tests/${PROJECT_NAME}") configure_file(template.test.in "${exe}.test" @ONLY) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${exe}.test" DESTINATION "${CMAKE_INSTALL_DATADIR}/installed-tests/${PROJECT_NAME}" ) endif() endforeach() if(SDL2IMAGE_TESTS_INSTALL) install( TARGETS ${ALL_TESTS} DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}/installed-tests/${PROJECT_NAME}" ) install( FILES ${RESOURCE_FILES} DESTINATION "${CMAKE_INSTALL_LIBEXECDIR}/installed-tests/${PROJECT_NAME}" ) endif() SDL2_image-2.8.8/test/Makefile.am0000664000000000000000000000270214444663624013360 0ustar00testmetadir = $(datadir)/installed-tests/$(PACKAGE_TARNAME) testexecdir = $(libexecdir)/installed-tests/$(PACKAGE_TARNAME) test_programs = \ testimage \ $(NULL) testimage_CPPFLAGS = -I$(top_srcdir)/include testimage_SOURCES = main.c testimage_LDADD = \ ../libSDL2_image.la \ $(SDL_LIBS) \ -lSDL2_test \ $(NULL) AM_TESTS_ENVIRONMENT = \ SDL_TEST_SRCDIR=$(abs_srcdir) \ SDL_TEST_BUILDDIR=$(abs_builddir) \ SDL_VIDEODRIVER=dummy \ $(NULL) EXTRA_DIST = \ CMakeLists.txt Makefile.os2 template.test.in if INSTALL_TESTS testexec_PROGRAMS = $(test_programs) else noinst_PROGRAMS = $(test_programs) endif TESTS = $(test_programs) if INSTALL_TESTS dist_testexec_DATA = \ palette.bmp \ palette.gif \ sample.avif \ sample.bmp \ sample.cur \ sample.ico \ sample.jpg \ sample.jxl \ sample.pcx \ sample.png \ sample.pnm \ sample.qoi \ sample.tga \ sample.tif \ sample.webp \ sample.xcf \ sample.xpm \ svg-class.bmp \ svg-class.svg \ svg.bmp \ svg.svg \ svg64.bmp \ $(NULL) all-local: generatetestmeta generatetestmeta: rm -f *.test set -e; for exe in $(test_programs); do \ sed \ -e 's#@installedtestsdir@#$(testexecdir)#g' \ -e "s#@exe@#$$exe#g" \ < $(srcdir)/template.test.in > $$exe.test; \ done install-data-hook: installtestmeta installtestmeta: generatetestmeta install -d $(DESTDIR)$(testmetadir) install -m644 *.test $(DESTDIR)$(testmetadir) clean-local: rm -f *.test rm -f save.jpg save.bmp CompareSurfaces*.bmp endif SDL2_image-2.8.8/test/Makefile.os20000664000000000000000000000210114241744634013453 0ustar00# change DEPS_INC in order to point to the dependency headers. DEPS_INC=-IC:\SDL2DEV\h\SDL2 # change DEPS_LIB in order to point to the dependency libraries. DEPS_LIB=C:\SDL2DEV\lib TARGETS = testimage.exe OBJS = $(TARGETS:.exe=.obj) all: $(TARGETS) INCPATH = -I$(%WATCOM)/h/os2 -I$(%WATCOM)/h -I.. $(DEPS_INC) CFLAGS_DEF = $(INCPATH) -bt=os2 -d0 -q -bm -5s -fp5 -fpi87 -sg -oteanbmier CFLAGS_EXE = $(CFLAGS_DEF) CFLAGS = $(CFLAGS_EXE) -ei -5s CFLAGS+= -DLOAD_JPG -DLOAD_PNG -DLOAD_BMP -DLOAD_GIF -DLOAD_LBM & -DLOAD_PCX -DLOAD_PNM -DLOAD_TGA -DLOAD_XCF -DLOAD_XPM & -DLOAD_XV -DLOAD_SVG -DLOAD_TIF -DLOAD_WEBP -DLOAD_QOI #CFLAGS+= -DLOAD_AVIF #CFLAGS+= -DLOAD_JXL CFLAGS+= -DSDL_IMAGE_SAVE_JPG=1 CFLAGS+= -DSDL_IMAGE_SAVE_PNG=1 LIBPATH = .. LIBS = SDL2img.lib SDL2test.lib SDL2.lib .obj.exe: wlink SYS os2v2 libpath $(LIBPATH) libpath $(DEPS_LIB) lib {$(LIBS)} op q file {$<} N $* testimage.obj: main.c wcc386 $(CFLAGS) -fo=$^@ $< clean: .SYMBOLIC @if exist *.obj rm *.obj @if exist *.err rm *.err distclean: clean .SYMBOLIC @if exist *.exe rm *.exe SDL2_image-2.8.8/test/README.md0000664000000000000000000000612214243162555012575 0ustar00`SDL2_image` automated test =========================== Build-time tests ---------------- Configure with `--enable-tests` (Autotools) or `-DSDL2IMAGE_TESTS` (CMake). Run build-time tests in the usual way, for example `make check` (Autotools), or `ctest` or `make test` (CMake). "As-installed" tests -------------------- The tests can be installed alongside the library, allowing them to be run on a target platform as a somewhat realistic equivalent of how a game would behave. Configure with `--enable-tests --enable-installed-tests` (Autotools) or `-DSDL2IMAGE_TESTS -DSDL2IMAGE_TESTS_INSTALL` (CMake). The tests and their required resource files are installed into `${libexecdir}/installed-tests/SDL2_image`. When run, they will write to the current working directory: create a temporary directory and run them from there if necessary. On platforms where `SDL_GetBasePath()` returns the directory containing the executable, the whole `installed-tests/SDL2_image` directory can be copied to any location and `testimage` can be run from there. Metadata describing the tests is installed in `${datadir}/installed-tests/SDL2_image`. This can be used to run the tests with `ginsttest-runner` from [gnome-desktop-testing](https://gitlab.gnome.org/GNOME/gnome-desktop-testing), or any implementation of the same [specification](https://wiki.gnome.org/Initiatives/GnomeGoals/InstalledTests). Asserting format support ------------------------ By default, for each format that was configured at build-time, the test asserts that the format can be loaded or saved as appropriate. Formats that were not enabled at build-time are not tested. This automatic behaviour is not always desirable: for example, if there was a build-system regression that accidentally disabled WEBP support, or if the required WEBP library was missing or not detected at build time, then the test would would report WEBP as unsupported but would not fail. To check that the intended formats are actually supported, distributors can set some environment variables before running the tests: * For each *format* where loading can be supported, if `SDL_IMAGE_TEST_REQUIRE_LOAD_` + *format* is set to 1, the test will fail unless `SDL2_image` can load *format*. (For example, `export SDL_IMAGE_TEST_REQUIRE_LOAD_WEBP=1`.) * For each *format* where saving can be supported (`JPG` or `PNG`), if `SDL_IMAGE_TEST_REQUIRE_SAVE_` + *format* is set to 1, the test will fail unless `SDL2_image` can load *format*. (For example, `export SDL_IMAGE_TEST_REQUIRE_SAVE_JPG=1`.) Setting these variables to 0 causes them to be ignored, returning to the automatic behaviour where the formats that were enabled at build-time will be tested. These variables are not set automatically for supported formats by the Autotools or CMake build system, because that would defeat their purpose of detecting build-system bugs. The abbreviation used for *format* is the same as in the `supported[]` table in `IMG.c`, for example `JPG` or `TIF`. `JPEG` and `TIFF` are ignored. On operating systems where environment variables are case-sensitive, *format* must be in upper-case. SDL2_image-2.8.8/test/main.c0000664000000000000000000006631414751445211012414 0ustar00/* Copyright 1997-2025 Sam Lantinga Copyright 2022 Collabora Ltd. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely. */ #include "SDL_image.h" #include "SDL.h" #include "SDL_test.h" #if defined(SDL_FILESYSTEM_OS2) || defined(SDL_FILESYSTEM_WINDOWS) static const char pathsep[] = "\\"; #elif defined(SDL_FILESYSTEM_RISCOS) static const char pathsep[] = "."; #else static const char pathsep[] = "/"; #endif #if defined(__APPLE__) && !defined(SDL_IMAGE_USE_COMMON_BACKEND) # define USING_IMAGEIO 1 #else # define USING_IMAGEIO 0 #endif typedef enum { TEST_FILE_DIST, TEST_FILE_BUILT } TestFileType; static SDL_bool GetStringBoolean(const char *value, SDL_bool default_value) { if (!value || !*value) { return default_value; } if (*value == '0' || SDL_strcasecmp(value, "false") == 0) { return SDL_FALSE; } return SDL_TRUE; } /* * Return the absolute path to a resource file, similar to GLib's * g_test_build_filename(). * * If type is TEST_FILE_DIST, look for it in $SDL_TEST_SRCDIR or next * to the executable. * * If type is TEST_FILE_BUILT, look for it in $SDL_TEST_BUILDDIR or next * to the executable. * * Fails and returns NULL if out of memory. */ static char * GetTestFilename(TestFileType type, const char *file) { const char *env; char *base = NULL; char *path = NULL; SDL_bool needPathSep = SDL_TRUE; if (type == TEST_FILE_DIST) { env = SDL_getenv("SDL_TEST_SRCDIR"); } else { env = SDL_getenv("SDL_TEST_BUILDDIR"); } if (env != NULL) { base = SDL_strdup(env); if (base == NULL) { SDL_OutOfMemory(); return NULL; } } if (base == NULL) { base = SDL_GetBasePath(); /* SDL_GetBasePath() guarantees a trailing path separator */ needPathSep = SDL_FALSE; } if (base != NULL) { size_t len = SDL_strlen(base) + SDL_strlen(pathsep) + SDL_strlen(file) + 1; path = SDL_malloc(len); if (path == NULL) { SDL_OutOfMemory(); return NULL; } if (needPathSep) { SDL_snprintf(path, len, "%s%s%s", base, pathsep, file); } else { SDL_snprintf(path, len, "%s%s", base, file); } SDL_free(base); } else { path = SDL_strdup(file); if (path == NULL) { SDL_OutOfMemory(); return NULL; } } return path; } static SDLTest_CommonState *state; typedef struct { const char *name; const char *sample; const char *reference; int w; int h; int tolerance; int initFlag; SDL_bool canLoad; SDL_bool canSave; int (SDLCALL * checkFunction)(SDL_RWops *src); SDL_Surface *(SDLCALL * loadFunction)(SDL_RWops *src); } Format; static const Format formats[] = { { "AVIF", "sample.avif", "sample.bmp", 23, 42, 300, IMG_INIT_AVIF, #ifdef LOAD_AVIF SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isAVIF, IMG_LoadAVIF_RW, }, { "BMP", "sample.bmp", "sample.png", 23, 42, 0, /* lossless */ 0, /* no initialization */ #ifdef LOAD_BMP SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isBMP, IMG_LoadBMP_RW, }, { "CUR", "sample.cur", "sample.bmp", 23, 42, 0, /* lossless */ 0, /* no initialization */ #ifdef LOAD_BMP SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isCUR, IMG_LoadCUR_RW, }, { "GIF", "palette.gif", "palette.bmp", 23, 42, 0, /* lossless */ 0, /* no initialization */ #if USING_IMAGEIO || defined(LOAD_GIF) SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isGIF, IMG_LoadGIF_RW, }, { "ICO", "sample.ico", "sample.bmp", 23, 42, 0, /* lossless */ 0, /* no initialization */ #ifdef LOAD_BMP SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isICO, IMG_LoadICO_RW, }, { "JPG", "sample.jpg", "sample.bmp", 23, 42, 100, IMG_INIT_JPG, #if (USING_IMAGEIO && defined(JPG_USES_IMAGEIO)) || defined(SDL_IMAGE_USE_WIC_BACKEND) || defined(LOAD_JPG) SDL_TRUE, #else SDL_FALSE, #endif SDL_IMAGE_SAVE_JPG, IMG_isJPG, IMG_LoadJPG_RW, }, #if 0 /* Different versions of JXL yield different output images */ { "JXL", "sample.jxl", "sample.bmp", 23, 42, 300, IMG_INIT_JXL, #ifdef LOAD_JXL SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isJXL, IMG_LoadJXL_RW, }, #endif #if 0 { "LBM", "sample.lbm", "sample.bmp", 23, 42, 0, /* lossless? */ 0, /* no initialization */ #ifdef LOAD_LBM SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isLBM, IMG_LoadLBM_RW, }, #endif { "PCX", "sample.pcx", "sample.bmp", 23, 42, 0, /* lossless? */ 0, /* no initialization */ #ifdef LOAD_PCX SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isPCX, IMG_LoadPCX_RW, }, { "PNG", "sample.png", "sample.bmp", 23, 42, 0, /* lossless */ IMG_INIT_PNG, #if (USING_IMAGEIO && defined(PNG_USES_IMAGEIO)) || defined(SDL_IMAGE_USE_WIC_BACKEND) || defined(LOAD_PNG) SDL_TRUE, #else SDL_FALSE, #endif #ifdef SDL_IMAGE_SAVE_PNG SDL_IMAGE_SAVE_PNG ? SDL_TRUE : SDL_FALSE, #else SDL_FALSE, #endif IMG_isPNG, IMG_LoadPNG_RW, }, { "PNM", "sample.pnm", "sample.bmp", 23, 42, 0, /* lossless */ 0, /* no initialization */ #ifdef LOAD_PNM SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isPNM, IMG_LoadPNM_RW, }, { "QOI", "sample.qoi", "sample.bmp", 23, 42, 0, /* lossless */ 0, /* no initialization */ #ifdef LOAD_QOI SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isQOI, IMG_LoadQOI_RW, }, { "SVG", "svg.svg", "svg.bmp", 32, 32, 100, 0, /* no initialization */ #ifdef LOAD_SVG SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isSVG, IMG_LoadSVG_RW, }, { "SVG-sized", "svg.svg", "svg64.bmp", 64, 64, 100, 0, /* no initialization */ #ifdef LOAD_SVG SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isSVG, IMG_LoadSVG_RW, }, { "SVG-class", "svg-class.svg", "svg-class.bmp", 82, 82, 0, /* lossless? */ 0, /* no initialization */ #ifdef LOAD_SVG SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isSVG, IMG_LoadSVG_RW, }, { "TGA", "sample.tga", "sample.bmp", 23, 42, 0, /* lossless? */ 0, /* no initialization */ #if USING_IMAGEIO || defined(LOAD_TGA) SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ NULL, IMG_LoadTGA_RW, }, { "TIF", "sample.tif", "sample.bmp", 23, 42, 0, /* lossless */ IMG_INIT_TIF, #if USING_IMAGEIO || defined(SDL_IMAGE_USE_WIC_BACKEND) || defined(LOAD_TIF) SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isTIF, IMG_LoadTIF_RW, }, { "WEBP", "sample.webp", "sample.bmp", 23, 42, 0, /* lossless */ IMG_INIT_WEBP, #ifdef LOAD_WEBP SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isWEBP, IMG_LoadWEBP_RW, }, { "XCF", "sample.xcf", "sample.bmp", 23, 42, 0, /* lossless */ 0, /* no initialization */ #ifdef LOAD_XCF SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isXCF, IMG_LoadXCF_RW, }, { "XPM", "sample.xpm", "sample.bmp", 23, 42, 0, /* lossless */ 0, /* no initialization */ #ifdef LOAD_XPM SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isXPM, IMG_LoadXPM_RW, }, #if 0 { "XV", "sample.xv", "sample.bmp", 23, 42, 0, /* lossless? */ 0, /* no initialization */ #ifdef LOAD_XV SDL_TRUE, #else SDL_FALSE, #endif SDL_FALSE, /* can save */ IMG_isXV, IMG_LoadXV_RW, }, #endif }; static SDL_bool StrHasSuffix(const char *str, const char *suffix) { size_t str_len = SDL_strlen(str); size_t suffix_len = SDL_strlen(suffix); return (str_len >= suffix_len && SDL_strcmp(str + (str_len - suffix_len), suffix) == 0); } typedef enum { LOAD_CONVENIENCE = 0, LOAD_RW, LOAD_TYPED_RW, LOAD_FORMAT_SPECIFIC, LOAD_SIZED } LoadMode; /* Convert to RGBA for comparison, if necessary */ static SDL_bool ConvertToRgba32(SDL_Surface **surface_p) { if ((*surface_p)->format->format != SDL_PIXELFORMAT_RGBA32) { SDL_Surface *temp; temp = SDL_ConvertSurfaceFormat(*surface_p, SDL_PIXELFORMAT_RGBA32, 0); SDLTest_AssertCheck(temp != NULL, "Converting to RGBA should succeed (%s)", SDL_GetError()); if (temp == NULL) { return SDL_FALSE; } SDL_FreeSurface(*surface_p); *surface_p = temp; } return SDL_TRUE; } static void DumpPixels(const char *filename, SDL_Surface *surface) { const unsigned char *pixels = surface->pixels; const unsigned char *p; size_t w, h, pitch; size_t i, j; SDL_Log("%s:\n", filename); if (surface->format->palette) { size_t n = 0; if (surface->format->palette->ncolors >= 0) { n = (size_t) surface->format->palette->ncolors; } SDL_Log(" Palette:\n"); for (i = 0; i < n; i++) { SDL_Log(" RGBA[0x%02x] = %02x%02x%02x%02x\n", (unsigned) i, surface->format->palette->colors[i].r, surface->format->palette->colors[i].g, surface->format->palette->colors[i].b, surface->format->palette->colors[i].a); } } if (surface->w < 0) { SDL_Log(" Invalid width %d\n", surface->w); return; } if (surface->h < 0) { SDL_Log(" Invalid height %d\n", surface->h); return; } if (surface->pitch < 0) { SDL_Log(" Invalid pitch %d\n", surface->pitch); return; } w = (size_t) surface->w; h = (size_t) surface->h; pitch = (size_t) surface->pitch; SDL_Log(" Pixels:\n"); for (j = 0; j < h; j++) { SDL_Log(" "); for (i = 0; i < w; i++) { p = pixels + (j * pitch) + (i * surface->format->BytesPerPixel); switch (surface->format->BitsPerPixel) { case 1: case 4: case 8: SDL_Log("%02x ", *p); break; case 12: case 15: case 16: SDL_Log("%02x", *p++); SDL_Log("%02x ", *p); break; case 24: SDL_Log("%02x", *p++); SDL_Log("%02x", *p++); SDL_Log("%02x ", *p); break; case 32: SDL_Log("%02x", *p++); SDL_Log("%02x", *p++); SDL_Log("%02x", *p++); SDL_Log("%02x ", *p); break; } } SDL_Log("\n"); } } static void FormatLoadTest(const Format *format, LoadMode mode) { SDL_Surface *reference = NULL; SDL_Surface *surface = NULL; SDL_RWops *src = NULL; char *filename = GetTestFilename(TEST_FILE_DIST, format->sample); char *refFilename = GetTestFilename(TEST_FILE_DIST, format->reference); int initResult = 0; int diff; if (!SDLTest_AssertCheck(filename != NULL, "Building filename should succeed (%s)", SDL_GetError())) { goto out; } if (!SDLTest_AssertCheck(refFilename != NULL, "Building ref filename should succeed (%s)", SDL_GetError())) { goto out; } if (StrHasSuffix(format->reference, ".bmp")) { reference = SDL_LoadBMP(refFilename); if (!SDLTest_AssertCheck(reference != NULL, "Loading reference should succeed (%s)", SDL_GetError())) { goto out; } } else if (StrHasSuffix (format->reference, ".png")) { #ifdef LOAD_PNG reference = IMG_Load(refFilename); if (!SDLTest_AssertCheck(reference != NULL, "Loading reference should succeed (%s)", SDL_GetError())) { goto out; } #endif } if (format->initFlag) { initResult = IMG_Init(format->initFlag); if (!SDLTest_AssertCheck(initResult != 0, "Initialization should succeed (%s)", SDL_GetError())) { goto out; } SDLTest_AssertCheck(initResult & format->initFlag, "Expected at least bit 0x%x set, got 0x%x", format->initFlag, initResult); } if (mode != LOAD_CONVENIENCE) { src = SDL_RWFromFile(filename, "rb"); SDLTest_AssertCheck(src != NULL, "Opening %s should succeed (%s)", filename, SDL_GetError()); if (src == NULL) goto out; } switch (mode) { case LOAD_CONVENIENCE: surface = IMG_Load(filename); break; case LOAD_RW: if (format->checkFunction != NULL) { SDL_RWops *ref_src; int check; ref_src = SDL_RWFromFile(refFilename, "rb"); SDLTest_AssertCheck(ref_src != NULL, "Opening %s should succeed (%s)", refFilename, SDL_GetError()); if (ref_src != NULL) { check = format->checkFunction(ref_src); SDLTest_AssertCheck(!check, "Should not detect %s as %s -> %d", refFilename, format->name, check); SDL_RWclose(ref_src); } } if (format->checkFunction != NULL) { int check = format->checkFunction(src); SDLTest_AssertCheck(check, "Should detect %s as %s -> %d", filename, format->name, check); } surface = IMG_Load_RW(src, SDL_TRUE); src = NULL; /* ownership taken */ break; case LOAD_TYPED_RW: surface = IMG_LoadTyped_RW(src, SDL_TRUE, format->name); src = NULL; /* ownership taken */ break; case LOAD_FORMAT_SPECIFIC: surface = format->loadFunction(src); break; case LOAD_SIZED: if (SDL_strcmp(format->name, "SVG-sized") == 0) { surface = IMG_LoadSizedSVG_RW(src, 64, 64); } break; } if (!SDLTest_AssertCheck(surface != NULL, "Load %s (%s)", filename, SDL_GetError())) { goto out; } SDLTest_AssertCheck(surface->w == format->w, "Expected width %d px, got %d", format->w, surface->w); SDLTest_AssertCheck(surface->h == format->h, "Expected height %d px, got %d", format->h, surface->h); if (GetStringBoolean(SDL_getenv("SDL_IMAGE_TEST_DEBUG"), SDL_FALSE)) { DumpPixels(filename, surface); } if (reference != NULL) { ConvertToRgba32(&reference); ConvertToRgba32(&surface); diff = SDLTest_CompareSurfaces(surface, reference, format->tolerance); SDLTest_AssertCheck(diff == 0, "Surface differed from reference by at most %d in %d pixels", format->tolerance, diff); if (diff != 0 || GetStringBoolean(SDL_getenv("SDL_IMAGE_TEST_DEBUG"), SDL_FALSE)) { DumpPixels(filename, surface); DumpPixels(refFilename, reference); } } out: if (surface != NULL) { SDL_FreeSurface(surface); } if (reference != NULL) { SDL_FreeSurface(reference); } if (src != NULL) { SDL_RWclose(src); } if (refFilename != NULL) { SDL_free(refFilename); } if (filename != NULL) { SDL_free(filename); } if (initResult) { IMG_Quit(); } } static void FormatSaveTest(const Format *format, SDL_bool rw) { char *refFilename = GetTestFilename(TEST_FILE_DIST, "sample.bmp"); char filename[64] = { 0 }; SDL_Surface *reference = NULL; SDL_Surface *surface = NULL; SDL_RWops *dest = NULL; int initResult = 0; int diff; int result; SDL_snprintf(filename, sizeof(filename), "save%s.%s", rw ? "Rwops" : "", format->name); if (!SDLTest_AssertCheck(refFilename != NULL, "Building ref filename should succeed (%s)", SDL_GetError())) { goto out; } reference = SDL_LoadBMP(refFilename); if (!SDLTest_AssertCheck(reference != NULL, "Loading reference should succeed (%s)", SDL_GetError())) { goto out; } if (format->initFlag) { initResult = IMG_Init(format->initFlag); if (!SDLTest_AssertCheck(initResult != 0, "Initialization should succeed (%s)", SDL_GetError())) { goto out; } SDLTest_AssertCheck(initResult & format->initFlag, "Expected at least bit 0x%x set, got 0x%x", format->initFlag, initResult); } if (SDL_strcmp (format->name, "PNG") == 0) { if (rw) { dest = SDL_RWFromFile(filename, "wb"); result = IMG_SavePNG_RW(reference, dest, SDL_FALSE); SDL_RWclose(dest); } else { result = IMG_SavePNG(reference, filename); } } else if (SDL_strcmp(format->name, "JPG") == 0) { if (rw) { dest = SDL_RWFromFile(filename, "wb"); result = IMG_SaveJPG_RW(reference, dest, SDL_FALSE, 90); SDL_RWclose(dest); } else { result = IMG_SaveJPG(reference, filename, 90); } } else { SDLTest_AssertCheck(SDL_FALSE, "How do I save %s?", format->name); goto out; } SDLTest_AssertCheck(result == 0, "Save %s (%s)", filename, SDL_GetError()); if (format->canLoad) { surface = IMG_Load(filename); if (!SDLTest_AssertCheck(surface != NULL, "Load %s (%s)", "saved file", SDL_GetError())) { goto out; } ConvertToRgba32(&reference); ConvertToRgba32(&surface); SDLTest_AssertCheck(surface->w == format->w, "Expected width %d px, got %d", format->w, surface->w); SDLTest_AssertCheck(surface->h == format->h, "Expected height %d px, got %d", format->h, surface->h); diff = SDLTest_CompareSurfaces(surface, reference, format->tolerance); SDLTest_AssertCheck(diff == 0, "Surface differed from reference by at most %d in %d pixels", format->tolerance, diff); if (diff != 0 || GetStringBoolean(SDL_getenv("SDL_IMAGE_TEST_DEBUG"), SDL_FALSE)) { DumpPixels(filename, surface); DumpPixels(refFilename, reference); } } out: if (surface != NULL) { SDL_FreeSurface(surface); } if (reference != NULL) { SDL_FreeSurface(reference); } if (refFilename != NULL) { SDL_free(refFilename); } if (initResult) { IMG_Quit(); } } static void FormatTest(const Format *format) { SDL_bool forced; char envVar[64] = { 0 }; SDL_snprintf(envVar, sizeof(envVar), "SDL_IMAGE_TEST_REQUIRE_LOAD_%s", format->name); forced = GetStringBoolean(SDL_getenv(envVar), SDL_FALSE); if (forced) { SDLTest_AssertCheck(format->canLoad, "%s loading should be enabled", format->name); } if (format->canLoad || forced) { SDLTest_Log("Testing ability to load format %s", format->name); if (SDL_strcmp(format->name, "SVG-sized") == 0) { FormatLoadTest(format, LOAD_SIZED); } else { FormatLoadTest(format, LOAD_CONVENIENCE); if (SDL_strcmp(format->name, "TGA") == 0) { SDLTest_Log("SKIP: Recognising %s by magic number is not supported", format->name); } else { FormatLoadTest(format, LOAD_RW); } FormatLoadTest(format, LOAD_TYPED_RW); if (format->loadFunction != NULL) { FormatLoadTest(format, LOAD_FORMAT_SPECIFIC); } } } else { SDLTest_Log("Format %s is not supported", format->name); } SDL_snprintf(envVar, sizeof(envVar), "SDL_IMAGE_TEST_REQUIRE_SAVE_%s", format->name); forced = GetStringBoolean(SDL_getenv(envVar), SDL_FALSE); if (forced) { SDLTest_AssertCheck(format->canSave, "%s saving should be enabled", format->name); } if (format->canSave || forced) { SDLTest_Log("Testing ability to save format %s", format->name); FormatSaveTest(format, SDL_FALSE); FormatSaveTest(format, SDL_TRUE); } else { SDLTest_Log("Saving format %s is not supported", format->name); } } static int TestFormats(void *arg) { size_t i; for (i = 0; i < SDL_arraysize(formats); i++) { FormatTest(&formats[i]); } return TEST_COMPLETED; } static const SDLTest_TestCaseReference formatsTestCase = { TestFormats, "Images", "Load and save various image formats", TEST_ENABLED }; static const SDLTest_TestCaseReference *testCases[] = { &formatsTestCase, NULL }; static SDLTest_TestSuiteReference testSuite = { "img", NULL, testCases, NULL }; static SDLTest_TestSuiteReference *testSuites[] = { &testSuite, NULL }; /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */ static void quit(int rc) { SDLTest_CommonQuit(state); exit(rc); } int main(int argc, char *argv[]) { int result; int testIterations = 1; Uint64 userExecKey = 0; char *userRunSeed = NULL; char *filter = NULL; int i, done; SDL_Event event; /* Initialize test framework */ state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO); if (!state) { return 1; } /* Parse commandline */ for (i = 1; i < argc;) { int consumed; consumed = SDLTest_CommonArg(state, i); if (consumed == 0) { consumed = -1; if (SDL_strcasecmp(argv[i], "--iterations") == 0) { if (argv[i + 1]) { testIterations = SDL_atoi(argv[i + 1]); if (testIterations < 1) testIterations = 1; consumed = 2; } } else if (SDL_strcasecmp(argv[i], "--execKey") == 0) { if (argv[i + 1]) { SDL_sscanf(argv[i + 1], "%" SDL_PRIu64, &userExecKey); consumed = 2; } } else if (SDL_strcasecmp(argv[i], "--seed") == 0) { if (argv[i + 1]) { userRunSeed = SDL_strdup(argv[i + 1]); consumed = 2; } } else if (SDL_strcasecmp(argv[i], "--filter") == 0) { if (argv[i + 1]) { filter = SDL_strdup(argv[i + 1]); consumed = 2; } } } if (consumed < 0) { #if SDL_VERSION_ATLEAST(2, 0, 10) static const char *options[] = { "[--iterations #]", "[--execKey #]", "[--seed string]", "[--filter suite_name|test_name]", NULL }; SDLTest_CommonLogUsage(state, argv[0], options); #else SDLTest_CommonUsage(state); #endif quit(1); } i += consumed; } /* Initialize common state */ if (!SDLTest_CommonInit(state)) { quit(2); } /* Create the windows, initialize the renderers */ for (i = 0; i < state->num_windows; ++i) { SDL_Renderer *renderer = state->renderers[i]; SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderClear(renderer); } /* Call Harness */ result = SDLTest_RunSuites(testSuites, (const char *)userRunSeed, userExecKey, (const char *)filter, testIterations); /* Empty event queue */ done = 0; for (i=0; i<100; i++) { while (SDL_PollEvent(&event)) { SDLTest_CommonEvent(state, &event, &done); } SDL_Delay(10); } /* Clean up */ SDL_free(userRunSeed); SDL_free(filter); /* Shutdown everything */ quit(result); return(result); } /* vi: set ts=4 sw=4 expandtab: */ SDL2_image-2.8.8/test/palette.bmp0000664000000000000000000000233214241727434013454 0ustar00BMкъ|*№aaџџџBGRs3™3f™3ЬЬff3ff3™f33Ьf33fЬ3fџ3™33џf™33џ™3f333™f™f3™™f™™ff™3                                                                               SDL2_image-2.8.8/test/palette.gif0000664000000000000000000000107014241727434013441 0ustar00GIF87a*„ ™3™f3Ь3Ьffff3f™3Ь333fЬf3f3џ3™3џ3™fџ33™3f3™33f™f™™3™™ff3™f3™33™33™33™33™33™33™33™3,*ўр"ŠЭ2˜Э0ЄjЋ уЙšЫcЎУ# B//ЅœKШ{щА­‘JэСЁшс†*нSp€p›Т”.ъы!{^bkW,"НШ…‹RЯ\@ПoщЈy^p{r,/Rwy{ rTЃт,‰€j™YЗj­j€*ŸJЁтœZЖkYLFмZ+З@`S­жЛ^ѕR 8ЈTœmпFиЫјяи*шl`ё Ља{…Х‹3ЈJВЎзВ яeРkЕ{3F0ЁђокЃxܘъlжX`­{3aН‹‘О``8qж!;SDL2_image-2.8.8/test/sample.avif0000664000000000000000000000050214241727434013443 0ustar00ftypavifavifmif1miafъmeta!hdlrpictpitm"ilocD@4#iinfinfeav01jiprpKipcocolrnclx € av1C ispe*pixiipma‚„1“<2”9&pi&qh&rf&td(ub(w`(y^)z[)|Z*}W*U+S+‚Q,„P,…N-‡L.ˆJ.ŠG.ŒE/C0A0‘>0’=$mn%nl&pi&rg&te'uc(wa(w`(y^)z\)|Z)~W+€U+S,‚Q,„O,†M-ˆJ-ˆH.ŠG/ŒE/C/ŽA#jq$lp%mn%nk%pi&qg&se'uc'wa(x_)z\){Z*}Y*~W*€U+S,ƒQ,„O,…M-ˆK.‰H.‹F/ŒD"gu#is#jq$lo%nm%pj%qi&rh&se'uc(wa)y_)y]){[*}X*V+T+‚R+ƒP,…O-†M-ˆK.‰H"dx"ex#gv#is#kq$lo$ml%pk&qi&sf'ud'vb(w`(x_)z]){[*}X*~V+€T+R,ƒP,…N-‡L b|!cz!dy"fw#ht#jr$lp$lo%nm&pk&qh&rg'td'vb(w`)y^){[*|Y*~W*W*€T+‚R+ƒO _€ `!a}!cz"ex#fv"ht$ir$kp$mm$ol%pi&rg&sf'td'ub(w`(y^({[*|Y*~W*U+S]„]‚ _€ `~!b{!dy"fw"gw"ht$jr$kp$mm%ok%pi&qg&sf'uc'va(x_(y])z[)|Y*~WY‰[‡]„^‚ _€ a~!c{!dz"fx"gv#is#kq$ln$mm%nl&pi&rh&se'uc(w`(x_(y]*{[WŒX‰Y‡[…]ƒ ^ _€!a}!c{"dy"fx#gv#it#jq%lo%mm&pj%ph&rf'se'uc(va(y_SUŽW‹XŠZ‡[…]ƒ_ a!b}!c{!ex"fw"gu#is#jq$lo$nl&oj&qh&rf&ud(vbQ“R‘TVŒW‹Y‰Z‡[†]ƒ ^ `!b|!cz"ex"gv#ht#jr$kp$mo$nm&pj&qh&sgN˜P•Q“R‘TVWŠYˆZ†\„^‚`€ `~!a}!dz"ex"fv"ht#jr$lp$mn%ol%pjK›L™N–P•Q’STVXŠY‰Z†\„ ^‚ _€ a}!c{!ez"ew"gv#ht#jr$kp%mmIŸIKšM™N—P•R’SUŽW‹YŠYˆZ‡\„ ]‚ _€ a}!c|"dz"fx"gv#is#jqFЂG IžJ›LšN—N–P”R’SUWŒXŠZ‡[†]ƒ^ `~ a~ b{!dz"fw"guCЇDЄFЃHŸIžJœLšN˜O•Q“S‘TUŽVŒXŠZˆ[…]ƒ_!`~!a|!c{"dx@ЊBЇCЅEЃGЁHŸIJ›LšN—O•Q“R‘TVŒWŠY‰[†\…]ƒ _ `~!b|=­?Ћ@ЊAЈCЅEЃFЁHŸJœKšM˜O–O•Q“R‘TVŒW‹Y‰Z†\„ ^‚`€:Б<Џ>Ќ?ЋAЈAЇDЅEЃFЁHžJK›L˜N—P”R’SUVŒWŠY‰[†\„8Е9Г:Б<Џ=Ќ?Њ@ЈCЇDЄFЂG HŸIK›M™O–P•Q’SUŽV‹XŠZˆ5Й6Ж8Е9В<А<Џ>Ќ@ЋAЈBІDЅEЂH HžK›M™M—N—P”R’STŽV‹2М3Л5Й6Ж8Д:В;А=­>ЋAЊBЈCІDЄFЂG IJ›L™M˜P–Q“R‘UŽ/Р1О2М5К6И6З9Е:В<А=Ў>Ћ@ЊAЈCЅEЃGЁHŸIžK›LšN˜O•Q“,Ф-Т/Р1О3М4К6И8Е9Г;Б<Ў=Ў?Ќ@ЊAЇCЅEЃFЁHŸJL›M™O—)Ч,Х-У.С0П1Н2М5К6З7Е9Д;Б<Џ=­?Ћ@ЉBЇDІEЄGЁIŸJKš &Ь(Щ*Ш+Х-У.С0П2М3Л5И7Ж7Е9Г;Б<Џ>­@ЊAЉCІDЄFЂG Iž %Я &Э 'Ъ)Щ )Ш+Х-Ф.С0П2М3Л5Й6Ж8Д:В;А=­>­@ЊAЈCЇDЄFЂ !г #б $Я &Ь 'Ъ (Щ*Ц,Ф.Т/Р1П1М3Л5Й7Ж8Е9В;А=Ў?Ћ@ЉAЇCЅ ж д "в #а $Я &Ь(Ы)Щ+Ц,Х.Т/Р1О2М4К5З7Ж8Д9В<А=Ў>Ћ@Њ л и з д "в $а $Ю 'Ь (Ъ*Ш+Ц,Х.Т/Р1О2Л4Й5З7Е9Г:Б=Џ=Ў о л к и ж д "в $а %Ю &Ы(Ъ)Ч+Х-У.С0П1О3М4Й6З7Е9Г;Бтро м й з е !г #в $Я %Ю &Ь (Ъ*Ш+Х-У.С0О1Н3Л5Й7З7Ехуспо м к з е !г #б $Я %Э 'Ъ)Ш+Ц+Х,У/С0П1Н4Л5Йъшхусп н л й ж е !д "б $Я %Э 'Ы(Ш*Ч,Ф-Т0Р1О1Ньышчцусп н л й з д "в #а %Ю &Э'Ы )Ш*Ч,Ф.Т/Р ђ яэыщчфтрон к и ж е !в #а %Ю &Ы (Ъ*Ч+Х,Фє ѓ № я эыщцфтром к з ж д !в #а %Ю 'Ы(Щ *Чљїє ђ № юыщшцфтро л к и е !д #б $Я %Э &Ьќљјіє ђ № юьъчцуспн л к з е !г "б $ЯSDL2_image-2.8.8/test/sample.cur0000664000000000000000000000626614241727434013324 0ustar00*  (T'vb(va(x_)z\){[)}Y+V*€T+R,„O,…N-†L-ˆI.‰H.‹F/ŒD0ŽB0@1‘=1“;1”:2•72˜5&sf'ud'ub'w`)y^(z\*}Y*}Y*W*T+‚R,ƒP,…N-‡L-ˆJ.‰G/‹E/C/ŽA0?0‘>1“<2”9&pi&qh&rf&td(ub(w`(y^)z[)|Z*}W*U+S+‚Q,„P,…N-‡L.ˆJ.ŠG.ŒE/C0A0‘>0’=$mn%nl&pi&rg&te'uc(wa(w`(y^)z\)|Z)~W+€U+S,‚Q,„O,†M-ˆJ-ˆH.ŠG/ŒE/C/ŽA#jq$lp%mn%nk%pi&qg&se'uc'wa(x_)z\){Z*}Y*~W*€U+S,ƒQ,„O,…M-ˆK.‰H.‹F/ŒD"gu#is#jq$lo%nm%pj%qi&rh&se'uc(wa)y_)y]){[*}X*V+T+‚R+ƒP,…O-†M-ˆK.‰H"dx"ex#gv#is#kq$lo$ml%pk&qi&sf'ud'vb(w`(x_)z]){[*}X*~V+€T+R,ƒP,…N-‡L b|!cz!dy"fw#ht#jr$lp$lo%nm&pk&qh&rg'td'vb(w`)y^){[*|Y*~W*W*€T+‚R+ƒO _€ `!a}!cz"ex#fv"ht$ir$kp$mm$ol%pi&rg&sf'td'ub(w`(y^({[*|Y*~W*U+S]„]‚ _€ `~!b{!dy"fw"gw"ht$jr$kp$mm%ok%pi&qg&sf'uc'va(x_(y])z[)|Y*~WY‰[‡]„^‚ _€ a~!c{!dz"fx"gv#is#kq$ln$mm%nl&pi&rh&se'uc(w`(x_(y]*{[WŒX‰Y‡[…]ƒ ^ _€!a}!c{"dy"fx#gv#it#jq%lo%mm&pj%ph&rf'se'uc(va(y_SUŽW‹XŠZ‡[…]ƒ_ a!b}!c{!ex"fw"gu#is#jq$lo$nl&oj&qh&rf&ud(vbQ“R‘TVŒW‹Y‰Z‡[†]ƒ ^ `!b|!cz"ex"gv#ht#jr$kp$mo$nm&pj&qh&sgN˜P•Q“R‘TVWŠYˆZ†\„^‚`€ `~!a}!dz"ex"fv"ht#jr$lp$mn%ol%pjK›L™N–P•Q’STVXŠY‰Z†\„ ^‚ _€ a}!c{!ez"ew"gv#ht#jr$kp%mmIŸIKšM™N—P•R’SUŽW‹YŠYˆZ‡\„ ]‚ _€ a}!c|"dz"fx"gv#is#jqFЂG IžJ›LšN—N–P”R’SUWŒXŠZ‡[†]ƒ^ `~ a~ b{!dz"fw"guCЇDЄFЃHŸIžJœLšN˜O•Q“S‘TUŽVŒXŠZˆ[…]ƒ_!`~!a|!c{"dx@ЊBЇCЅEЃGЁHŸIJ›LšN—O•Q“R‘TVŒWŠY‰[†\…]ƒ _ `~!b|=­?Ћ@ЊAЈCЅEЃFЁHŸJœKšM˜O–O•Q“R‘TVŒW‹Y‰Z†\„ ^‚`€:Б<Џ>Ќ?ЋAЈAЇDЅEЃFЁHžJK›L˜N—P”R’SUVŒWŠY‰[†\„8Е9Г:Б<Џ=Ќ?Њ@ЈCЇDЄFЂG HŸIK›M™O–P•Q’SUŽV‹XŠZˆ5Й6Ж8Е9В<А<Џ>Ќ@ЋAЈBІDЅEЂH HžK›M™M—N—P”R’STŽV‹2М3Л5Й6Ж8Д:В;А=­>ЋAЊBЈCІDЄFЂG IJ›L™M˜P–Q“R‘UŽ/Р1О2М5К6И6З9Е:В<А=Ў>Ћ@ЊAЈCЅEЃGЁHŸIžK›LšN˜O•Q“,Ф-Т/Р1О3М4К6И8Е9Г;Б<Ў=Ў?Ќ@ЊAЇCЅEЃFЁHŸJL›M™O—)Ч,Х-У.С0П1Н2М5К6З7Е9Д;Б<Џ=­?Ћ@ЉBЇDІEЄGЁIŸJKš &Ь(Щ*Ш+Х-У.С0П2М3Л5И7Ж7Е9Г;Б<Џ>­@ЊAЉCІDЄFЂG Iž %Я &Э 'Ъ)Щ )Ш+Х-Ф.С0П2М3Л5Й6Ж8Д:В;А=­>­@ЊAЈCЇDЄFЂ !г #б $Я &Ь 'Ъ (Щ*Ц,Ф.Т/Р1П1М3Л5Й7Ж8Е9В;А=Ў?Ћ@ЉAЇCЅ ж д "в #а $Я &Ь(Ы)Щ+Ц,Х.Т/Р1О2М4К5З7Ж8Д9В<А=Ў>Ћ@Њ л и з д "в $а $Ю 'Ь (Ъ*Ш+Ц,Х.Т/Р1О2Л4Й5З7Е9Г:Б=Џ=Ў о л к и ж д "в $а %Ю &Ы(Ъ)Ч+Х-У.С0П1О3М4Й6З7Е9Г;Бтро м й з е !г #в $Я %Ю &Ь (Ъ*Ш+Х-У.С0О1Н3Л5Й7З7Ехуспо м к з е !г #б $Я %Э 'Ъ)Ш+Ц+Х,У/С0П1Н4Л5Йъшхусп н л й ж е !д "б $Я %Э 'Ы(Ш*Ч,Ф-Т0Р1О1Ньышчцусп н л й з д "в #а %Ю &Э'Ы )Ш*Ч,Ф.Т/Р ђ яэыщчфтрон к и ж е !в #а %Ю &Ы (Ъ*Ч+Х,Фє ѓ № я эыщцфтром к з ж д !в #а %Ю 'Ы(Щ *Чљїє ђ № юыщшцфтро л к и е !д #б $Я %Э &Ьќљјіє ђ № юьъчцуспн л к з е !г "б $ЯSDL2_image-2.8.8/test/sample.ico0000664000000000000000000000626614241727434013305 0ustar00*  (T'vb(va(x_)z\){[)}Y+V*€T+R,„O,…N-†L-ˆI.‰H.‹F/ŒD0ŽB0@1‘=1“;1”:2•72˜5&sf'ud'ub'w`)y^(z\*}Y*}Y*W*T+‚R,ƒP,…N-‡L-ˆJ.‰G/‹E/C/ŽA0?0‘>1“<2”9&pi&qh&rf&td(ub(w`(y^)z[)|Z*}W*U+S+‚Q,„P,…N-‡L.ˆJ.ŠG.ŒE/C0A0‘>0’=$mn%nl&pi&rg&te'uc(wa(w`(y^)z\)|Z)~W+€U+S,‚Q,„O,†M-ˆJ-ˆH.ŠG/ŒE/C/ŽA#jq$lp%mn%nk%pi&qg&se'uc'wa(x_)z\){Z*}Y*~W*€U+S,ƒQ,„O,…M-ˆK.‰H.‹F/ŒD"gu#is#jq$lo%nm%pj%qi&rh&se'uc(wa)y_)y]){[*}X*V+T+‚R+ƒP,…O-†M-ˆK.‰H"dx"ex#gv#is#kq$lo$ml%pk&qi&sf'ud'vb(w`(x_)z]){[*}X*~V+€T+R,ƒP,…N-‡L b|!cz!dy"fw#ht#jr$lp$lo%nm&pk&qh&rg'td'vb(w`)y^){[*|Y*~W*W*€T+‚R+ƒO _€ `!a}!cz"ex#fv"ht$ir$kp$mm$ol%pi&rg&sf'td'ub(w`(y^({[*|Y*~W*U+S]„]‚ _€ `~!b{!dy"fw"gw"ht$jr$kp$mm%ok%pi&qg&sf'uc'va(x_(y])z[)|Y*~WY‰[‡]„^‚ _€ a~!c{!dz"fx"gv#is#kq$ln$mm%nl&pi&rh&se'uc(w`(x_(y]*{[WŒX‰Y‡[…]ƒ ^ _€!a}!c{"dy"fx#gv#it#jq%lo%mm&pj%ph&rf'se'uc(va(y_SUŽW‹XŠZ‡[…]ƒ_ a!b}!c{!ex"fw"gu#is#jq$lo$nl&oj&qh&rf&ud(vbQ“R‘TVŒW‹Y‰Z‡[†]ƒ ^ `!b|!cz"ex"gv#ht#jr$kp$mo$nm&pj&qh&sgN˜P•Q“R‘TVWŠYˆZ†\„^‚`€ `~!a}!dz"ex"fv"ht#jr$lp$mn%ol%pjK›L™N–P•Q’STVXŠY‰Z†\„ ^‚ _€ a}!c{!ez"ew"gv#ht#jr$kp%mmIŸIKšM™N—P•R’SUŽW‹YŠYˆZ‡\„ ]‚ _€ a}!c|"dz"fx"gv#is#jqFЂG IžJ›LšN—N–P”R’SUWŒXŠZ‡[†]ƒ^ `~ a~ b{!dz"fw"guCЇDЄFЃHŸIžJœLšN˜O•Q“S‘TUŽVŒXŠZˆ[…]ƒ_!`~!a|!c{"dx@ЊBЇCЅEЃGЁHŸIJ›LšN—O•Q“R‘TVŒWŠY‰[†\…]ƒ _ `~!b|=­?Ћ@ЊAЈCЅEЃFЁHŸJœKšM˜O–O•Q“R‘TVŒW‹Y‰Z†\„ ^‚`€:Б<Џ>Ќ?ЋAЈAЇDЅEЃFЁHžJK›L˜N—P”R’SUVŒWŠY‰[†\„8Е9Г:Б<Џ=Ќ?Њ@ЈCЇDЄFЂG HŸIK›M™O–P•Q’SUŽV‹XŠZˆ5Й6Ж8Е9В<А<Џ>Ќ@ЋAЈBІDЅEЂH HžK›M™M—N—P”R’STŽV‹2М3Л5Й6Ж8Д:В;А=­>ЋAЊBЈCІDЄFЂG IJ›L™M˜P–Q“R‘UŽ/Р1О2М5К6И6З9Е:В<А=Ў>Ћ@ЊAЈCЅEЃGЁHŸIžK›LšN˜O•Q“,Ф-Т/Р1О3М4К6И8Е9Г;Б<Ў=Ў?Ќ@ЊAЇCЅEЃFЁHŸJL›M™O—)Ч,Х-У.С0П1Н2М5К6З7Е9Д;Б<Џ=­?Ћ@ЉBЇDІEЄGЁIŸJKš &Ь(Щ*Ш+Х-У.С0П2М3Л5И7Ж7Е9Г;Б<Џ>­@ЊAЉCІDЄFЂG Iž %Я &Э 'Ъ)Щ )Ш+Х-Ф.С0П2М3Л5Й6Ж8Д:В;А=­>­@ЊAЈCЇDЄFЂ !г #б $Я &Ь 'Ъ (Щ*Ц,Ф.Т/Р1П1М3Л5Й7Ж8Е9В;А=Ў?Ћ@ЉAЇCЅ ж д "в #а $Я &Ь(Ы)Щ+Ц,Х.Т/Р1О2М4К5З7Ж8Д9В<А=Ў>Ћ@Њ л и з д "в $а $Ю 'Ь (Ъ*Ш+Ц,Х.Т/Р1О2Л4Й5З7Е9Г:Б=Џ=Ў о л к и ж д "в $а %Ю &Ы(Ъ)Ч+Х-У.С0П1О3М4Й6З7Е9Г;Бтро м й з е !г #в $Я %Ю &Ь (Ъ*Ш+Х-У.С0О1Н3Л5Й7З7Ехуспо м к з е !г #б $Я %Э 'Ъ)Ш+Ц+Х,У/С0П1Н4Л5Йъшхусп н л й ж е !д "б $Я %Э 'Ы(Ш*Ч,Ф-Т0Р1О1Ньышчцусп н л й з д "в #а %Ю &Э'Ы )Ш*Ч,Ф.Т/Р ђ яэыщчфтрон к и ж е !в #а %Ю &Ы (Ъ*Ч+Х,Фє ѓ № я эыщцфтром к з ж д !в #а %Ю 'Ы(Щ *Чљїє ђ № юыщшцфтро л к и е !д #б $Я %Э &Ьќљјіє ђ № юьъчцуспн л к з е !г "б $ЯSDL2_image-2.8.8/test/sample.jpg0000664000000000000000000000110214241727434013273 0ustar00џиџрJFIFddџлC   (1#%(:3=<9387@H\N@DWE78PmQW_bghg>Mqypdx\egcџлC//cB8BccccccccccccccccccccccccccccccccccccccccccccccccccџТ*џФџФџк Чхћ$T †РЈЈ4чPms ЖѓЈџФ@џкџФџк?Ye–Ye–Ye–Ye™›џФ@џк?џФ@џк?џФ@џк?!џк ;w-#ДњЗџФ@џк?ЊЊЊЃџФ@џк?џФџк? ‚ ‚ ‚ ˆ‹џйSDL2_image-2.8.8/test/sample.jxl0000664000000000000000000000024314241727434013315 0ustar00џ H,€„тBˆLUЈPeмрх\Я—:,Іm\ghЋm KEЦБI:C’XФЭюЛЯ,Ÿ<€b~Т:zŸэОЏз9 к|,šg2ЯQ_хgЯkП‡‡MTХLЭTU”LЯ†T( Rр:,X’bŒдтАzиyё”ЛЩO€sЃ˜ѕы(јЧцЫd)5—Г€тSDL2_image-2.8.8/test/sample.pcx0000664000000000000000000000631614243442304013311 0ustar00 )ddСќСљСјСіСєСђС№СюСьСъСчСцСуСсСпСнСлСкСзСеСгСбСЯ !"$ТУТТУТТ Т Т СљСїСєСђС№СюСыСщСшСцСфСтСрСоСлСкСиСеСдСбСЯСЭСЬ Т !#$%&ТТУУТТ Т Т СєСѓС№СяСэСыСщСцСфСтСрСоСмСкСзСжСдСвСаСЮСЫСЩСЧ  !#%'(*ТТТУТ Т У  СђСяСэСыСщСчСфСтСрСоСнСкСиСжСеСвСаСЮСЫСЪСЧСХСФ Т !#%&(*+,УТТТ Т Т У ТСьСыСшСчСцСуСсСпСнСлСйСзСдСвСаСЮСЭСЫСШСЧСФСТСРТ "#%&')*,./ФТ Т У  ТСъСшСхСуСсСпСнСлСйСжСеСдСбСЯСЭСЫСШСЧСФСТСРОН!"$%'(*,-0Т1ТТУ Т У УТСхСуСсСпСоСмСкСзСеСгСбСЯСЭСЪСШСЦСХСУССПНЛЙ!#$%')Т+,/0145ТТУ Т У УТТСтСрСоСмСйСзСеСгСвСЯСЮСЬСЪСШСХСУССОНЛЙЗЕ!#$%&(*+-.0135Т7Т У Т У ТТТУСоСлСкСиСжСдСвСаСЮСЫСЪСЧСХСУССПОМЙЗЕГБТ "$%&()+-.0134679;Т Ф Т ТТУТТСлСиСзСдСвСаСЮСЬСЪСШСЦСХСТСРОЛЙЗЕГБЏЎ "Т$'(*+,./124579:Т= Т У Т УТУТТТСжСдСвСаСЯСЬСЫСЩСЦСХСТСРОМКЗЖДВАЎЋЊ "#$&()+,./1245789<=>@ У УУТТУСгСбСЯСЬСЪСЩСЦСФСТСРПМЛЙЖЕВАЎЋЉЇЅ!#$&'(*,./Т135789;=?@AC Т У ТТУТУСЯСЭСЪСЩСШСХСФССПМЛЙЖДВАТ­ЊЈЇЄЂ%&'Т)+-.023568:;=>@ACDF Т  УТТУТУСЬСЩСШСХСУССПМЛИЖЕГБЏ­ЊЉІЄЂ ž&(*+-.0235Т79;<>@ACDFGI УТТТТТТУТСЧСХСУССПНМКЗЕДБЏ­ЋЉЇІЄЁŸš),-.0125679;<=?@BDEGIJKТУУТТТУТСФСТСРОМКИЕГБТЎЌЊЇЅЃЁŸ›™—,-/134689;<=?@ACEFHJLMOТУТТТТУТСРОМКИЗЕВАЎЋЊЈЅЃЁŸž›š˜•“/125Т69:<=>@ACEGHIKLNOQФТТТУТТМЛЙЖДВА­ЋЊЈІЄЂ ›™˜–“‘Ž23568:;=>ABCDFGIJLMPQRUТТТУФУУЙЖЕВАЏЌЋЈІЅЂ ž›™Т—”’Ž‹5689Т<>@ABDEТHKТMNPRSTVТУУТТУТТЕГБЏЌЊЈЇЄЂ Ÿ›™–•’Ž‹Šˆ89:<=?@CDFGHIKMOPQSUVXZУТУТТУТБЏЌЋЈЇЅЃЁž›˜—”’ŒŠ‰†„:<>?ТADEFHJKLNPRSUVWY[\УТТТТТУТТ­ЋЊЈЅЃЁŸœš˜–•“‘Œ‹‰†„‚€=?@ACEFHJKMТOQRTVWYZ\^`ТТУТТТТТТ ЊЇЅЃЁŸ›š—•“‘ŒŠ‰†…ƒ~|@BCEGHIJLNOQRTVWY[\]_`bТУТТТТТТТ !ЇЄЃŸžœš˜•“‘ŽŒŠˆ…ƒ~|{xCDFHIJLNOQSTUVXZ[]_`acdТТУТУТТУ!"Ђ ž›š—–”’ŒŠ‡†ƒТ~{zwuFGIJLТNPRSUWXZ[]^`abdfgТТТУУУУ !Т"Ÿš™—•’Ž‹Šˆ‡„‚€}|zxvsqТIKMNPRSUWТYZ\]_acdfgijТУУТУ !У"Т#›™–•’Š‰†„‚€}{zwvtrpmKLNPQSTVXYZ\^_acТeghjkmУУУ Т!Т"Т#$%˜•“‘Šˆ†„‚€~}zxvtrpnljNPQRTVWYZ\^Т`adefhjlmopТТТТУ Т!У"#Т$Т%“‘Œ‹‰‡†ƒ|zxvtrpomjhgQRTVWYZ[]^`bceghjkmnpqsТТТТТ Т!Т"Т#У$У&Ž‹Š‡…ƒ}{xwusqoljhfdbSUWXZ[]_abcefgijlnoqruvТУ У!Т"Т#Т$Ф&(Œ‰‡…ƒ€}{yxvtqomjhfeca_WXY[]^_acdfgijlmТprsuvyТТ Т!Т"У#Т%&%&Т'Т(‰‡„‚€~{zxvsqnmlihec`_][Y[]^_acdfgiklmnprsuwxy{УТ Т!Т"Т#Т$%У&'У(*„‚€~{yТwtrpmkigfca_][YWТ]_`bdfghjkmopqsuvxyz|~Т Т!У"У$Т%Т&Т'Т(Т)*€}zxvtrpmligfdb`^[YWUS_`acefhikmoprstuwy{|~Т Т!"#"Ф$%Т&Т'У(У*+|zywtrpomkhgdb`^[YТWTRObcdfhjТlnpqrtvwy{|~€‚ƒ Т!"Т#Т$%У&Т'(Т)Ф*Т+Тxvsqolkifdb`_][XVTRPNLdegiklmpqsuvwxz{}~€ƒ…‡Т"У#Т$%Т&Т'Т(Т)Т*Т+Т,-usqomjiheca_][XVTRPOMKHgijlnpqrsuwТy{}‚ƒ…†ˆ‰"Т#$У%Т&'(У)Т*У+,Т-.qpnkigeca_\ZYWUSQOMKHFDjlmnpqsuwxz{}~€ƒ„…ˆ‰‹Œ#$У%Т&Т'(Т)У*+У,-Т./nligeca`^\ZWUSQOMJHGECAmnprtuТwyz|~€‚„†ТˆŠŒŽ$%У&'У(У)Т+У,Т-.У/ihfdb`^[ZWUSQPNLJGECA>=pqrtuwyz|}‚„…‡ˆŠŒ‘’Ф&У(Т)Т*Т+Т,-У./У0fdb`^\ТYWTRPNLJGECA?><9sТuwyzТ}‚ƒ…‡ˆ‰‹Ž‘“”&У')(Ф*+Т,Т-.У/Т012ba_\[YVTRONLIHFDB@=;:75Тvxz{}€„…†ˆ‰‹ŒŽ‘“”•˜'Т(У)+*+Т,Т-Т./Т0У1Т2SDL2_image-2.8.8/test/sample.png0000664000000000000000000000152214241727434013305 0ustar00‰PNG  IHDR*зЌW„IDATHЧmVI‚#1“dџџС“цРbWвЗ,ƒ’љˆ&щЄ NИш‚‹N˜Є .8щ„“!˜X‘Є лDЯ_;ЮCtМђ‚‹&;Œ§йбШ КкТЋ4žУ>UWEtaћЂ‘zЅ \ РV…iUшОфўЗВsFž9-tТ}M"hŠ<•1;jќњK8|НDчЈз шlVЉп~Є„3чCџќzа™ћKŒя юgeŸђS'5UŠїХ—лG>GM~„ўфу3%Ксqж…QAѓЃ2‚QЕhфўД8OѓUџ@paa§„1BХ‰m‹Ž#—?$КpŒћ­Cп кOm*CЕѕQЙюЁъrЛж ŠКGЎпj˜ъ‡xљдmt"qy0с< ygЗЈv ругAšт{_[:FЎbж‚SћйАcНђ[Щyњ ГEGдBЊМi[oРьюБ›NW&ЩYT^цŸZЗср}Ц‹ЏбќMшn?RєюЕvEЖ|9учљJZЈЊ5}iнHшЬЎыЅћ‘0Ш'‹ˆXМЊ;з\rwŠЛlž7B№ѕрI};ЏЖоћйƒдКм ОјхГ­kо5щАѓ*ъИРхЭњžˆ‰Ž|fTЦ§YъБЯ*сшљ1ьѓ4ЕыОD`ВїА%ї‹ђЫир—%эg1ј•ш 'mqЈtј~ё3XўѓЦIЫШ+`ы`IENDЎB`‚SDL2_image-2.8.8/test/sample.pnm0000664000000000000000000000561514241727434013322 0ustar00P6 # Created by GIMP version 2.10.30 PNM plug-in 23 42 255 ќљјієђ № ю ьъчцуспнл к з е г! б" Я$ љїєђ № ю ыщшцфтрол к и е д! б# Я$ Э% Ь& єѓ № я э ыщцфтромк з ж д в! а# Ю% Ы' Щ(Ч* ђ я эыщчфтронк и ж е в! а# Ю% Ы& Ъ( Ч*Х+Ф,ьышчцуспн л й з д в" а# Ю% Э& Ы'Ш) Ч*Ф,Т.Р/ъшхуспн л й ж е д! б" Я$ Э% Ы' Ш(Ч*Ф,Т-Р0О1Н1хуспом к з е г! б# Я$ Э% Ъ' Ш)Ц+Х+У,С/П0Н1Л4Й5тром й з е г! в# Я$ Ю% Ь& Ъ( Ш*Х+У-С.О0Н1Л3Й5З7Е7о л к и ж д в" а$ Ю% Ы& Ъ(Ч)Х+У-С.П0О1М3Й4З6Е7Г9Б;л и з д в" а$ Ю$ Ь' Ъ( Ш*Ц+Х,Т.Р/О1Л2Й4З5Е7Г9Б:Џ=Ў=ж д в" а# Я$ Ь& Ы(Щ)Ц+Х,Т.Р/О1М2К4З5Ж7Д8В9А<Ў=Ћ>Њ@г! б# Я$ Ь& Ъ' Щ( Ц*Ф,Т.Р/П1М1Л3Й5Ж7Е8В9А;Ў=Ћ?Љ@ЇAЅCЯ% Э& Ъ' Щ)Ш) Х+Ф-С.П0М2Л3Й5Ж6Д8В:А;­=­>Њ@ЈAЇCЄDЂFЬ& Щ(Ш*Х+У-С.П0М2Л3И5Ж7Е7Г9Б;Џ<­>Њ@ЉAІCЄDЂF GžIЧ)Х,У-С.П0Н1М2К5З6Е7Д9Б;Џ<­=Ћ?Љ@ЇBІDЄEЁGŸIJšKФ,Т-Р/О1М3К4И6Е8Г9Б;Ў<Ў=Ќ?Њ@ЇAЅCЃEЁFŸHJ›L™M—OР/О1М2К5И6З6Е9В:А<Ў=Ћ>Њ@ЈAЅCЃEЁGŸHžI›KšL˜N•O“QМ2Л3Й5Ж6Д8В:А;­=Ћ>ЊAЈBІCЄDЂF GI›J™L˜M–P“Q‘RŽUЙ5Ж6Е8В9А<Џ<Ќ>Ћ@ЈAІBЅDЂE HžH›K™M—M—N”P’RSŽT‹VЕ8Г9Б:Џ<Ќ=Њ?Ј@ЇCЄDЂF GŸHI›K™M–O•P’QSŽU‹VŠXˆZБ:Џ<Ќ>Ћ?ЈAЇAЅDЃEЁFžHJ›K˜L—N”P’RSUŒVŠW‰Y†[„\­=Ћ?Њ@ЈAЅCЃEЁFŸHœJšK˜M–O•O“Q‘RTŒV‹W‰Y†Z„\‚^ €`Њ@ЇBЅCЃEЁGŸHI›JšL—N•O“Q‘RTŒVŠW‰Y†[…\ƒ]_ ~` |b!ЇCЄDЃFŸHžIœJšL˜N•O“Q‘STŽUŒVŠXˆZ…[ƒ]_~`!|a!{c!xd"ЂF GžI›JšL—N–N”P’RSUŒWŠX‡Z†[ƒ]^~` ~a {b zd!wf"ug"ŸIIšK™M—N•P’RSŽU‹WŠYˆY‡Z„\‚] €_ }a |c!zd"xf"vg"si#qj#›K™L–N•P’QSTVŠX‰Y†Z„\‚^ €_ }a {c!ze!we"vg"th#rj#pk$mm%˜N•P“Q‘RTVŠWˆY†Z„\‚^€`~` }a!zd!xe"vf"th"rj#pl$nm$lo%jp%“Q‘RTŒV‹W‰Y‡Z†[ƒ]^ ` |b!zc!xe"vg"th#rj#pk$om$mn$jp&hq&gs&SŽU‹WŠX‡Z…[ƒ]_a }b!{c!xe!wf"ug"si#qj#ol$ln$jo&hq&fr&du&bv(ŒW‰X‡Y…[ƒ]^ €_ }a!{c!yd"xf"vg#ti#qj#ol%mm%jp&hp%fr&es'cu'av(_y(‰Y‡[„]‚^€_ ~a {c!zd!xf"vg"si#qk#nl$mm$ln%ip&hr&es&cu'`w(_x(]y([{*„]‚]€_ ~` {b!yd!wf"wg"th"rj$pk$mm$ko%ip%gq&fs&cu'av'_x(]y([z)Y|)W~*€_ ` }a!zc!xe"vf#th"ri$pk$mm$lo$ip%gr&fs&dt'bu'`w(^y([{(Y|*W~*U*S+|b zc!yd!wf"th#rj#pl$ol$mn%kp&hq&gr&dt'bv'`w(^y)[{)Y|*W~*W*T€*R‚+Oƒ+xd"xe"vg#si#qk#ol$lm$kp%iq&fs&du'bv'`w(_x(]z)[{)X}*V~*T€+R+Pƒ,N…,L‡-ug"si#qj#ol$mn%jp%iq%hr&es&cu'aw(_y)]y)[{)X}*V*T+R‚+Pƒ+O…,M†-Kˆ-H‰.qj#pl$nm%kn%ip%gq&es&cu'aw'_x(\z)Z{)Y}*W~*U€*S+Qƒ,O„,M…,Kˆ-H‰.F‹.DŒ/nm$ln%ip&gr&et&cu'aw(`w(^y(\z)Z|)W~)U€+S+Q‚,O„,M†,Jˆ-Hˆ-GŠ.EŒ/C/AŽ/ip&hq&fr&dt&bu(`w(^y([z)Z|)W}*U*S+Q‚+P„,N…,L‡-Jˆ.GŠ.EŒ.C/A0>‘0=’0fs&du'bu'`w'^y)\z(Y}*Y}*W*T*R‚+Pƒ,N…,L‡-Jˆ-G‰.E‹/C/AŽ/?0>‘0<“19”2bv'av(_x(\z)[{)Y})V+T€*R+O„,N…,L†-Iˆ-H‰.F‹.DŒ/BŽ0@0=‘1;“1:”17•25˜2SDL2_image-2.8.8/test/sample.qoi0000664000000000000000000000271014241727434013311 0ustar00qoif*Ђ'Ђ6_OЂFNЂGЂFЂGNЂ7_Ђ6ЂFONOЂVЂ7NЂGOЂFўљЂFЂ7ЂFNЂ6ЂGЂWKЂFNЂGЂFЂVЂGЂ7^Ђ7*O]ўєЂWЁH^OЂFOЃ%6ЂFNN^OOЂFЂFЂ7OЂEўђ ЂGNЂF.Ѓ%O9O!ЁGЂGЂGЁGЂVЂ7O^ўь$Ђ7ЂWZ8NЂGЂGЂFOЂFЂG(ЂF_OЂ5>ЂFOўъOЂ687 NЂFЁH^#ЁHЂFNЂGЁH>Ђ6OЃ6OZўх8<^OЂ6ЁYЂFЂG*)Ђ7ЂGЂFZOЃ5ONЃ6OўтЂG Ђ7NЂF ЂV*3N6ЂG=ЂGNЂ7^ЂGЂGЂFJўо ЁGЂWЂFЂGЂGN/ЂWЁGЂGOЂF^ЂGЁGЂG%ЂGЂGўл ЁH"ЂFKЃ66N^Ђ7NЂHЁGЂFO%0NЃ6ZЂG(*2ЂWNЂ6_Ђ7NЂFOЂF!ЂVONЃ5<ЁHЂW ЂG*21^Ђ7ЂF NЂW YЂFЂ8]-ЂGЂGЂ6ONЂFўЯ% O1ЂWYЂ8ЂVЁGЂ6ЂGЁG.ЂG8Ђ6oЂ6OЂVЁGЂG2Ђ7ЁGЂ7Ђ7([0ЂGN_Ђ6NЂGNЂHўЧ)N_Ѓ6ЁG%ЂWЂ6:OЂF ЂFЂWNЂ6ЂGNЁHўФ,OЂGOЂFЂ604ЁH<ЂG ЂGЂFOЂF&ЂGOЂFЂGЃ5N[Ѓ6ЁGЂG<ЁHЂWMЂGN^Ђ7^ЂGЁHЂF^ЂGЁGЂG28Ђ7?ЃEOONЂFЂ8NЂF_Ѓ6ЁGNЃ&ўЙ5ЁHЂVЁH=:Ђ7ЂVOЂWЁGЃ6K,7JnЂ7ЂFONЂ7ўЕ80O:ЁGЂGNЂGN"(,7Ђ6_ЁGЂF ЂVЂGўБ::Ђ6_[Ђ6ЂW,ЁHЂV>Ђ7_NЂWЂ6Oў­=ЂF NN"Ђ6OЂF8ZЂG?ЂGЂ6_ЁG&ЂGЂEOЂG"NOЂV65?  _NЂGЁGЂGЁGЂW"$OЂGЂFЁGЂG _ЂGЁG(ЂFЁINЂVЁHO$'.Ђ7Z>Ђ8ЂVЂ7_(N/nЁGЂWЂ7NўŸIJЂ776ЂFЂ6ЂVK&OЂFЂ6ЂWOЂFNЂ7Nў›KOЂ7ЂUЁHЂG ЂFЁGЂFЂH0Ђ6<ЂV YЂVOЂFOЂ79Ђ7O ЂG&ЂFЂF/_>ONЂFЂGNЂGNN Ђ6ЂG_(OЂFЂ7NЂVNЂ8NЂVўSЂF_Ђ5ЁY(ЂFЂGO<Ђ6_NЂGЂ6ЁYЂFNЃ5ЁYўŒWЁHOЂFЂG.08ЂFЂGЂ6^ўfs&;JЂF MРЂ6OO,76ЁHЂGNЂGЂGЁH:[ЂF ЂFЂ8MOЃ&,OЂ6_ЂFЂGNЂ7ЂF^ЁHЃ5SDL2_image-2.8.8/test/sample.tga0000664000000000000000000000564714243442304013300 0ustar00 *'vb(va(x_)z\){[)}Y+V*€T+R,„O,…N-†L-ˆI.‰H.‹F/ŒD0ŽB0@1‘=1“;1”:2•72˜5&sf'ud'ub'w`)y^(z\*}Y*W*T+‚R,ƒP,…N-‡L-ˆJ.‰G/‹E/C/ŽA0?0‘>1“<2”9&pi&qh&rf&td(ub(w`(y^)z[)|Z*}W*U+S+‚Q,„P,…N-‡L.ˆJ.ŠG.ŒE/C0A0‘>0’=$mn%nl&pi&rg&te'uc(wa(w`(y^)z\)|Z)~W+€U+S,‚Q,„O,†M-ˆJ-ˆH.ŠG/ŒE/C/ŽA#jq$lp%mn%nk%pi&qg&se'uc'wa(x_)z\){Z*}Y*~W*€U+S,ƒQ,„O,…M-ˆK.‰H.‹F/ŒD"gu#is#jq$lo%nm%pj%qi&rh&se'uc(wa)y_)y]){[*}X*V+T+‚R+ƒP,…O-†M-ˆK.‰H"dx"ex#gv#is#kq$lo$ml%pk&qi&sf'ud'vb(w`(x_)z]){[*}X*~V+€T+R,ƒP,…N-‡L b|!cz!dy"fw#ht#jr$lp$lo%nm&pk&qh&rg'td'vb(w`)y^){[*|Y*~W*W*€T+‚R+ƒO _€ `!a}!cz"ex#fv"ht$ir$kp$mm$ol%pi&rg&sf'td'ub(w`(y^({[*|Y*~W*U+S]„]‚ _€ `~!b{!dy"fw"gw"ht$jr$kp$mm%ok%pi&qg&sf'uc'va(x_(y])z[)|Y*~WY‰[‡]„^‚ _€ a~!c{!dz"fx"gv#is#kq$ln$mm%nl&pi&rh&se'uc(w`(x_(y]*{[WŒX‰Y‡[…]ƒ ^ _€!a}!c{"dy"fx#gv#it#jq%lo%mm&pj%ph&rf'se'uc(va(y_SUŽW‹XŠZ‡[…]ƒ_ a!b}!c{!ex"fw"gu#is#jq$lo$nl&oj&qh&rf&ud(vbQ“R‘TVŒW‹Y‰Z‡[†]ƒ ^ `!b|!cz"ex"gv#ht#jr$kp$mo$nm&pj&qh&sgN˜P•Q“R‘TVWŠYˆZ†\„^‚`€ `~!a}!dz"ex"fv"ht#jr$lp$mn%ol%pjK›L™N–P•Q’STVXŠY‰Z†\„ ^‚ _€ a}!c{!ez"ew"gv#ht#jr$kp%mmIŸIKšM™N—P•R’SUŽW‹YŠYˆZ‡\„ ]‚ _€ a}!c|"dz"fx"gv#is#jqFЂG IžJ›LšN—N–P”R’SUWŒXŠZ‡[†]ƒ^ `~ a~ b{!dz"fw"guCЇDЄFЃHŸIžJœLšN˜O•Q“S‘TUŽVŒXŠZˆ[…]ƒ_!`~!a|!c{"dx@ЊBЇCЅEЃGЁHŸIJ›LšN—O•Q“R‘TVŒWŠY‰[†\…]ƒ _ `~!b|=­?Ћ@ЊAЈCЅEЃFЁHŸJœKšM˜O–O•Q“R‘TVŒW‹Y‰Z†\„ ^‚`€:Б<Џ>Ќ?ЋAЈAЇDЅEЃFЁHžJK›L˜N—P”R’SUVŒWŠY‰[†\„8Е9Г:Б<Џ=Ќ?Њ@ЈCЇDЄFЂG HŸIK›M™O–P•Q’SUŽV‹XŠZˆ5Й6Ж8Е9В<А<Џ>Ќ@ЋAЈBІDЅEЂH HžK›M™M—N—P”R’STŽV‹2М3Л5Й6Ж8Д:В;А=­>ЋAЊBЈCІDЄFЂG IJ›L™M˜P–Q“R‘UŽ/Р1О2М5К6И6З9Е:В<А=Ў>Ћ@ЊAЈCЅEЃGЁHŸIžK›LšN˜O•Q“,Ф-Т/Р1О3М4К6И8Е9Г;Б<Ў=Ў?Ќ@ЊAЇCЅEЃFЁHŸJL›M™O—)Ч,Х-У.С0П1Н2М5К6З7Е9Д;Б<Џ=­?Ћ@ЉBЇDІEЄGЁIŸJKš &Ь(Щ*Ш+Х-У.С0П2М3Л5И7Ж7Е9Г;Б<Џ>­@ЊAЉCІDЄFЂG Iž %Я &Э 'Ъ)Щ )Ш+Х-Ф.С0П2М3Л5Й6Ж8Д:В;А=­>­@ЊAЈCЇDЄFЂ !г #б $Я &Ь 'Ъ (Щ*Ц,Ф.Т/Р1П1М3Л5Й7Ж8Е9В;А=Ў?Ћ@ЉAЇCЅ ж д "в #а $Я &Ь(Ы)Щ+Ц,Х.Т/Р1О2М4К5З7Ж8Д9В<А=Ў>Ћ@Њ л и з д "в $а $Ю 'Ь (Ъ*Ш+Ц,Х.Т/Р1О2Л4Й5З7Е9Г:Б=Џ=Ў о л к и ж д "в $а %Ю &Ы(Ъ)Ч+Х-У.С0П1О3М4Й6З7Е9Г;Бтро м й з е !г #в $Я %Ю &Ь (Ъ*Ш+Х-У.С0О1Н3Л5Й7З7Ехуспо м к з е !г #б $Я %Э 'Ъ)Ш+Ц+Х,У/С0П1Н4Л5Йъшхусп н л й ж е !д "б $Я %Э 'Ы(Ш*Ч,Ф-Т0Р1О1Ньышчцусп н л й з д "в #а %Ю &Э'Ы )Ш*Ч,Ф.Т/Р ђ яэыщчфтрон к и ж е !в #а %Ю &Ы (Ъ*Ч+Х,Фє ѓ № я эыщцфтром к з ж д !в #а %Ю 'Ы(Щ *Чљїє ђ № юыщшцфтро л к и е !д #б $Я %Э &Ьќљјіє ђ № юьъчцуспн л к з е !г "б $ЯTRUEVISION-XFILE.SDL2_image-2.8.8/test/sample.tif0000664000000000000000000000610714241727434013307 0ustar00II*Z ќљјієђ № ю ьъчцуспнл к з е г! б" Я$ љїєђ № ю ыщшцфтрол к и е д! б# Я$ Э% Ь& єѓ № я э ыщцфтромк з ж д в! а# Ю% Ы' Щ(Ч* ђ я эыщчфтронк и ж е в! а# Ю% Ы& Ъ( Ч*Х+Ф,ьышчцуспн л й з д в" а# Ю% Э& Ы'Ш) Ч*Ф,Т.Р/ъшхуспн л й ж е д! б" Я$ Э% Ы' Ш(Ч*Ф,Т-Р0О1Н1хуспом к з е г! б# Я$ Э% Ъ' Ш)Ц+Х+У,С/П0Н1Л4Й5тром й з е г! в# Я$ Ю% Ь& Ъ( Ш*Х+У-С.О0Н1Л3Й5З7Е7о л к и ж д в" а$ Ю% Ы& Ъ(Ч)Х+У-С.П0О1М3Й4З6Е7Г9Б;л и з д в" а$ Ю$ Ь' Ъ( Ш*Ц+Х,Т.Р/О1Л2Й4З5Е7Г9Б:Џ=Ў=ж д в" а# Я$ Ь& Ы(Щ)Ц+Х,Т.Р/О1М2К4З5Ж7Д8В9А<Ў=Ћ>Њ@г! б# Я$ Ь& Ъ' Щ( Ц*Ф,Т.Р/П1М1Л3Й5Ж7Е8В9А;Ў=Ћ?Љ@ЇAЅCЯ% Э& Ъ' Щ)Ш) Х+Ф-С.П0М2Л3Й5Ж6Д8В:А;­=­>Њ@ЈAЇCЄDЂFЬ& Щ(Ш*Х+У-С.П0М2Л3И5Ж7Е7Г9Б;Џ<­>Њ@ЉAІCЄDЂF GžIЧ)Х,У-С.П0Н1М2К5З6Е7Д9Б;Џ<­=Ћ?Љ@ЇBІDЄEЁGŸIJšKФ,Т-Р/О1М3К4И6Е8Г9Б;Ў<Ў=Ќ?Њ@ЇAЅCЃEЁFŸHJ›L™M—OР/О1М2К5И6З6Е9В:А<Ў=Ћ>Њ@ЈAЅCЃEЁGŸHžI›KšL˜N•O“QМ2Л3Й5Ж6Д8В:А;­=Ћ>ЊAЈBІCЄDЂF GI›J™L˜M–P“Q‘RŽUЙ5Ж6Е8В9А<Џ<Ќ>Ћ@ЈAІBЅDЂE HžH›K™M—M—N”P’RSŽT‹VЕ8Г9Б:Џ<Ќ=Њ?Ј@ЇCЄDЂF GŸHI›K™M–O•P’QSŽU‹VŠXˆZБ:Џ<Ќ>Ћ?ЈAЇAЅDЃEЁFžHJ›K˜L—N”P’RSUŒVŠW‰Y†[„\­=Ћ?Њ@ЈAЅCЃEЁFŸHœJšK˜M–O•O“Q‘RTŒV‹W‰Y†Z„\‚^ €`Њ@ЇBЅCЃEЁGŸHI›JšL—N•O“Q‘RTŒVŠW‰Y†[…\ƒ]_ ~` |b!ЇCЄDЃFŸHžIœJšL˜N•O“Q‘STŽUŒVŠXˆZ…[ƒ]_~`!|a!{c!xd"ЂF GžI›JšL—N–N”P’RSUŒWŠX‡Z†[ƒ]^~` ~a {b zd!wf"ug"ŸIIšK™M—N•P’RSŽU‹WŠYˆY‡Z„\‚] €_ }a |c!zd"xf"vg"si#qj#›K™L–N•P’QSTVŠX‰Y†Z„\‚^ €_ }a {c!ze!we"vg"th#rj#pk$mm%˜N•P“Q‘RTVŠWˆY†Z„\‚^€`~` }a!zd!xe"vf"th"rj#pl$nm$lo%jp%“Q‘RTŒV‹W‰Y‡Z†[ƒ]^ ` |b!zc!xe"vg"th#rj#pk$om$mn$jp&hq&gs&SŽU‹WŠX‡Z…[ƒ]_a }b!{c!xe!wf"ug"si#qj#ol$ln$jo&hq&fr&du&bv(ŒW‰X‡Y…[ƒ]^ €_ }a!{c!yd"xf"vg#ti#qj#ol%mm%jp&hp%fr&es'cu'av(_y(‰Y‡[„]‚^€_ ~a {c!zd!xf"vg"si#qk#nl$mm$ln%ip&hr&es&cu'`w(_x(]y([{*„]‚]€_ ~` {b!yd!wf"wg"th"rj$pk$mm$ko%ip%gq&fs&cu'av'_x(]y([z)Y|)W~*€_ ` }a!zc!xe"vf#th"ri$pk$mm$lo$ip%gr&fs&dt'bu'`w(^y([{(Y|*W~*U*S+|b zc!yd!wf"th#rj#pl$ol$mn%kp&hq&gr&dt'bv'`w(^y)[{)Y|*W~*W*T€*R‚+Oƒ+xd"xe"vg#si#qk#ol$lm$kp%iq&fs&du'bv'`w(_x(]z)[{)X}*V~*T€+R+Pƒ,N…,L‡-ug"si#qj#ol$mn%jp%iq%hr&es&cu'aw(_y)]y)[{)X}*V*T+R‚+Pƒ+O…,M†-Kˆ-H‰.qj#pl$nm%kn%ip%gq&es&cu'aw'_x(\z)Z{)Y}*W~*U€*S+Qƒ,O„,M…,Kˆ-H‰.F‹.DŒ/nm$ln%ip&gr&et&cu'aw(`w(^y(\z)Z|)W~)U€+S+Q‚,O„,M†,Jˆ-Hˆ-GŠ.EŒ/C/AŽ/ip&hq&fr&dt&bu(`w(^y([z)Z|)W}*U*S+Q‚+P„,N…,L‡-Jˆ.GŠ.EŒ.C/A0>‘0=’0fs&du'bu'`w'^y)\z(Y}*Y}*W*T*R‚+Pƒ,N…,L‡-Jˆ-G‰.E‹/C/AŽ/?0>‘0<“19”2bv'av(_x(\z)[{)Y})V+T€*R+O„,N…,L†-Iˆ-H‰.F‹.DŒ/BŽ0@0=‘1;“1:”17•25˜2*0 €R  (  < (S6 ѓџcѓџcsample.bmpSDL2_image-2.8.8/test/sample.webp0000664000000000000000000000123414241727434013456 0ustar00RIFF”WEBPVP8L‡/@ "m› нПшˆшs(jлЪГќA_ˆў'VМiлт7;УЖmУєџ“Зˆ&Д™япљŽj‚IE‘Т’„ СВŠЄ`J bAZбњП!ŠUPB‚Д"ˆFа!ЋHИЕ5PY&š}1ЗT(Ј Qz#…$*-S(ЁX*Fмњ_PД‚TFHI@ЗbŠ4Ђ€BС*M"THвшєЇ2t" S‚ZЈ€Ui б1RKЋ(iмђRKLR$ *VЄ)ŠY$Е„ЬХ"­Н*§‰ЪмЎYTƒ HЊG…UЁˆU!AZB7„еЈ„Фі›"(5ЃJFEPI ЉЌ„ Tz!P!h7Є)нŒ Љ˜\#Є–ДPhЇ… ’Ж"%-Q3ЊД,Х*г-БЊ0е8V8ѕŠ$Л"ЈBкЄVTSŠ7RК)”IХJв$ЃbЕ%Ё„[HP *€/„”Т’„F5 –Ъ8­U@jЁе’Uі *–м%ЭJвЈ$Н*#i9‰I$e- R&(IrBь-’І"–Ќ‹ВH B…ЂЃYE" !э— ЄFI…•Дd IЃgDˆЄš„•QšВПJЂ%VPХвrކbвHEД„JЋЋ‚ДћПIН*–RX’‚рж …р)Ћ  Є2Jи-сPЕ„*ЈW…Ъ­EЕ …-$IH Ќеj*'BJ&Јь˜Ј(’LЂ@JЃP,!D[ЄX "9ЄД­DQБ !™„PP­NФђЕ{‘Š ЄJhЁьмДHЫci…j(„ZeSDL2_image-2.8.8/test/sample.xcf0000664000000000000000000000666714241727434013320 0ustar00gimp xcf v011*–BЧџцBЧџцШgimp-image-gridЌ(style solid) (fgcolor (color-rgba 0 0 0 1)) (bgcolor (color-rgba 1 1 1 1)) (xspacing 10) (yspacing 10) (spacing-unit inches) (xoffset 0) (yoffset 0) (offset-unit inches) ?* sample.bmpџ!?€ "     %$џџџџ#џџџџJ*f*~€Цќљјієђ№юьъчцуспнлкзегбЯљїєђ№юыщшцфтролкиедбЯЭЬєѓ№яэыщцфтромкзждваЮЫЩЧђяэыщчфтронкижеваЮЫЪЧХФьышчцуспнлйздваЮЭЫШЧФТРъшхуспнлйжедбЯЭЫШЧФТРОНхуспомкзегбЯЭЪШЦХУСПНЛЙтромйзегвЯЮЬЪШХУСОНЛЙЗЕолкиждваЮЫЪЧХУСПОМЙЗЕГБлиздваЮЬЪШЦХТРОЛЙЗЕГБЏЎждваЯЬЫЩЦХТРОМКЗЖДВАЎЋЊгбЯЬЪЩЦФТРПМЛЙЖЕВАЎЋЉЇЅЯЭЪЩШХФСПМЛЙЖДВА­­ЊЈЇЄЂЬЩШХУСПМЛИЖЕГБЏ­ЊЉІЄЂ žЧХУСПНМКЗЕДБЏ­ЋЉЇІЄЁŸšФТРОМКИЕГБЎЎЌЊЇЅЃЁŸ›™—РОМКИЗЕВАЎЋЊЈЅЃЁŸž›š˜•“МЛЙЖДВА­ЋЊЈІЄЂ ›™˜–“‘ŽЙЖЕВАЏЌЋЈІЅЂ ž›™——”’Ž‹ЕГБЏЌЊЈЇЄЂ Ÿ›™–•’Ž‹ŠˆБЏЌЋЈЇЅЃЁž›˜—”’ŒŠ‰†„­ЋЊЈЅЃЁŸœš˜–•“‘Œ‹‰†„‚€ЊЇЅЃЁŸ›š—•“‘ŒŠ‰†…ƒ~|ЇЄЃŸžœš˜•“‘ŽŒŠˆ…ƒ~|{xЂ ž›š—–”’ŒŠ‡†ƒ~~{zwuŸš™—•’Ž‹Šˆ‡„‚€}|zxvsq›™–•’Š‰†„‚€}{zwvtrpm˜•“‘Šˆ†„‚€~}zxvtrpnlj“‘Œ‹‰‡†ƒ|zxvtrpomjhgŽ‹Š‡…ƒ}{xwusqoljhfdbŒ‰‡…ƒ€}{yxvtqomjhfeca_‰‡„‚€~{zxvsqnmlihec`_][„‚€~{ywwtrpmkigfca_][YW€}zxvtrpmligfdb`^[YWUS|zywtrpomkhgdb`^[YWWTROxxvsqolkifdb`_][XVTRPNLusqomjiheca_][XVTRPOMKHqpnkigeca_\ZYWUSQOMKHFDnligeca`^\ZWUSQOMJHGECAihfdb`^[ZWUSQPNLJGECA>=fdb`^\YYWTRPNLJGECA?><9ba_\[YVTRONLIHFDB@=;:75€Ц !"$  !#$%&  !#%'(*  !#%&(*+, "#%&')*,./!"$%'(*,-011!#$%')++,/0145!#$%&(*+-.013577 "$%&()+-.0134679; "$$'(*+,./124579:== "#$&()+,./1245789<=>@!#$&'(*,./1135789;=?@AC%&'))+-.023568:;=>@ACDF&(*+-.0235779;<>@ACDFGI),-.0125679;<=?@BDEGIJK,-/134689;<=?@ACEFHJLMO/125669:<=>@ACEGHIKLNOQ23568:;=>ABCDFGIJLMPQRU5689<<>@ABDEHHKMMNPRSTV89:<=?@CDFGHIKMOPQSUVXZ:<>?AADEFHJKLNPRSUVWY[\=?@ACEFHJKMOOQRTVWYZ\^`@BCEGHIJLNOQRTVWY[\]_`bCDFHIJLNOQSTUVXZ[]_`acdFGIJLNNPRSUWXZ[]^`abdfgIIKMNPRSUWYYZ\]_acdfgijKLNPQSTVXYZ\^_aceeghjkmNPQRTVWYZ\^``adefhjlmopQRTVWYZ[]^`bceghjkmnpqsSUWXZ[]_abcefgijlnoqruvWXY[]^_acdfgijlmpprsuvyY[]^_acdfgiklmnprsuwxy{]]_`bdfghjkmopqsuvxyz|~_`acefhikmoprstuwy{|~bcdfhjllnpqrtvwy{|~€‚ƒdegiklmpqsuvwxz{}~€ƒ…‡gijlnpqrsuwyy{}‚ƒ…†ˆ‰jlmnpqsuwxz{}~€ƒ„…ˆ‰‹Œmnprtuwwyz|~€‚„†ˆˆŠŒŽpqrtuwyz|}‚„…‡ˆŠŒ‘’suuwyz}}‚ƒ…‡ˆ‰‹Ž‘“”vvxz{}€„…†ˆ‰‹ŒŽ‘“”•˜џџ  љ ў ё ќ  ћ  њ  њњ  ѓ    џ  џ ї   џ њ   џ  џ  ј ћ  § ў  џ џњ  џњћ  ќў ѕќџћњ§ќііџџќњ§ўќїіі ђ !§!ј"џ њ!""ўџ џ!"#ћџ !"#ѕ$%§ !!"ъ#$$%% !!""##$&§џ !"#$&є( !!""#%љ&%&''((є !!""##$$%&џ'(љ* !!"$%&'()ј* !!"#"$ћ%&&''(*і+ !!"##$$%&'§())*+"#$ь%&&''(())**++,,-"##$%&ў'()*+њ,--.#$%&'§())*џ+,њ-../$%&џ'()+,-џ./&()*+,џ-.џ/0џ&'ў)(*њ+,,--./0ћ12'(()є+*+,,--../0012SDL2_image-2.8.8/test/sample.xpm0000664000000000000000000002367514241727434013342 0ustar00/* XPM */ static char * sample_xpm[] = { "23 42 501 2", " c #FC0201", ". c #F90401", "+ c #F80502", "@ c #F60603", "# c #F40803", "$ c #F20903", "% c #F00B04", "& c #EE0D04", "* c #EC0F05", "= c #EA1005", "- c #E71206", "; c #E61307", "> c #E31507", ", c #E11707", "' c #DF1808", ") c #DD1908", "! c #DB1A09", "~ c #DA1C09", "{ c #D71E0A", "] c #D51F0A", "^ c #D3210B", "/ c #D1220C", "( c #CF240C", "_ c #F90402", ": c #F70602", "< c #F20A03", "[ c #EE0C04", "} c #EB0E04", "| c #E91005", "1 c #E81206", "2 c #E61207", "3 c #E41407", "4 c #E21507", "5 c #E01708", "6 c #DE1908", "7 c #D81E0A", "8 c #D5200B", "9 c #D4210B", "0 c #D1230C", "a c #CD250D", "b c #CC260C", "c c #F40802", "d c #F30A03", "e c #EF0C04", "f c #ED0D05", "g c #EB0F05", "h c #E91006", "i c #E61306", "j c #E21607", "k c #E01707", "l c #DC1A08", "m c #D61F0A", "n c #D4200B", "o c #D2210C", "p c #D0230C", "q c #CE250C", "r c #CB270D", "s c #C9280E", "t c #C72A0D", "u c #F20B03", "v c #ED0E05", "w c #E91105", "x c #E41506", "y c #D81D0A", "z c #D2210B", "A c #CE250D", "B c #CB260D", "C c #CA280D", "D c #C72A0E", "E c #C52B0F", "F c #C42C0F", "G c #EC0E04", "H c #E81106", "I c #E71307", "J c #E11607", "K c #DD1A09", "L c #DB1C09", "M c #D91D0A", "N c #D71F0A", "O c #D2220C", "P c #CD260D", "Q c #CB270E", "R c #C8290D", "S c #C22E0F", "T c #C02F10", "U c #E51306", "V c #DB1B09", "W c #D91D09", "X c #D61E0A", "Y c #CD250C", "Z c #C8280E", "` c #C42C0E", " . c #C22D0F", ".. c #C03010", "+. c #BE3111", "@. c #BD3111", "#. c #E51406", "$. c #DC1A09", "%. c #D71E09", "&. c #D51F0B", "*. c #CA270D", "=. c #C8290E", "-. c #C62B0E", ";. c #C52B0E", ">. c #C32C0F", ",. c #C12F0F", "'. c #BF3010", "). c #BD3110", "!. c #BB3411", "~. c #B93512", "{. c #D91C0A", "]. c #D71D0A", "^. c #D2230B", "/. c #CC260D", "(. c #C82A0E", "_. c #C32D0F", ":. c #C12E0F", "<. c #BE3010", "[. c #BB3311", "}. c #B73712", "|. c #B53712", "1. c #DE1909", "2. c #DA1C0A", "3. c #D4200A", "4. c #D2220B", "5. c #D0240C", "6. c #CA280E", "7. c #C7290E", "8. c #C12E10", "9. c #BE3110", "0. c #BC3311", "a. c #B93411", "b. c #B73612", "c. c #B33913", "d. c #B13B14", "e. c #D0240B", "f. c #CE240C", "g. c #CC270D", "h. c #C52C0E", "i. c #C02F0F", "j. c #BB3211", "k. c #B73512", "l. c #B13A13", "m. c #AF3D14", "n. c #AE3D14", "o. c #CB280E", "p. c #C9290E", "q. c #C52C0F", "r. c #C22E10", "s. c #BC3211", "t. c #BA3411", "u. c #B63712", "v. c #B43813", "w. c #B23913", "x. c #B03C13", "y. c #AB3E15", "z. c #AA4016", "A. c #C9280D", "B. c #C62A0E", "C. c #BF3110", "D. c #BC3111", "E. c #B93511", "F. c #B63713", "G. c #B53812", "H. c #B03B14", "I. c #AE3D15", "J. c #AB3F15", "K. c #A94016", "L. c #A74116", "M. c #A54316", "N. c #CF250C", "O. c #C42D0F", "P. c #BC3210", "Q. c #B63612", "R. c #B23A14", "S. c #AD3D14", "T. c #AD3E15", "U. c #AA4015", "V. c #A84116", "W. c #A74316", "X. c #A44416", "Y. c #A24617", "Z. c #B83512", "`. c #B53713", " + c #AF3C14", ".+ c #A94116", "++ c #A64316", "@+ c #A04717", "#+ c #9E4919", "$+ c #BA3512", "%+ c #B43913", "&+ c #B13B13", "*+ c #AD3D15", "=+ c #A74216", "-+ c #A64417", ";+ c #A44517", ">+ c #A14717", ",+ c #9F4918", "'+ c #9D4A18", ")+ c #9A4B19", "!+ c #BA3412", "~+ c #B83612", "{+ c #AE3C14", "]+ c #AC3F15", "^+ c #A54317", "/+ c #A34517", "(+ c #A14618", "_+ c #9F4818", ":+ c #9B4C19", "<+ c #994D1A", "[+ c #974F1A", "}+ c #BA3511", "|+ c #B83611", "1+ c #B53913", "2+ c #B23A13", "3+ c #B03C14", "4+ c #A84115", "5+ c #A14718", "6+ c #9E4918", "7+ c #9B4B19", "8+ c #9A4C19", "9+ c #984E1A", "0+ c #954F1B", "a+ c #93511B", "b+ c #AA4115", "c+ c #A84216", "d+ c #A64317", "e+ c #A44417", "f+ c #9D4919", "g+ c #9B4A19", "h+ c #994C19", "i+ c #984D1A", "j+ c #96501B", "k+ c #91521B", "l+ c #8E551C", "m+ c #B63613", "n+ c #B53813", "o+ c #B23914", "p+ c #AC3E15", "q+ c #AB4015", "r+ c #A64216", "s+ c #A54417", "t+ c #A24517", "u+ c #A04818", "v+ c #9E4819", "w+ c #974D1A", "x+ c #974E1A", "y+ c #94501B", "z+ c #92521B", "A+ c #90531C", "B+ c #8E541C", "C+ c #8B561D", "D+ c #B13A14", "E+ c #AC3D14", "F+ c #AA3F15", "G+ c #A84015", "H+ c #A24618", "I+ c #A04718", "J+ c #964F1A", "K+ c #95501B", "L+ c #92511B", "M+ c #90531B", "N+ c #8A581D", "O+ c #885A1E", "P+ c #AC3E14", "Q+ c #9E4818", "R+ c #9D4A19", "S+ c #984C1A", "T+ c #8D551C", "U+ c #8C561D", "V+ c #8A571D", "W+ c #89591E", "X+ c #865B1E", "Y+ c #845C1F", "Z+ c #AB3F14", "`+ c #A14617", " @ c #9C4A18", ".@ c #984D19", "+@ c #954F1A", "@@ c #8F541C", "#@ c #8C561C", "$@ c #8B571D", "%@ c #865A1E", "&@ c #825E20", "*@ c #80601F", "=@ c #9D4918", "-@ c #855C1F", ";@ c #835D1F", ">@ c #815F20", ",@ c #7E6020", "'@ c #7C6221", ")@ c #A34617", "!@ c #9C4A19", "~@ c #9A4C1A", "{@ c #91531C", "]@ c #8E551D", "^@ c #855B1E", "/@ c #815F1F", "(@ c #7E6021", "_@ c #7C6121", ":@ c #7B6321", "<@ c #786422", "[@ c #964E1A", "}@ c #8D551D", "|@ c #8C571D", "1@ c #875A1E", "2@ c #865B1F", "3@ c #815E1F", "4@ c #7E6120", "5@ c #7B6220", "6@ c #7A6421", "7@ c #776622", "8@ c #756722", "9@ c #95501A", "0@ c #8A591D", "a@ c #88591E", "b@ c #825D20", "c@ c #805F20", "d@ c #7D6120", "e@ c #7C6321", "f@ c #7A6422", "g@ c #786622", "h@ c #766722", "i@ c #736923", "j@ c #716A23", "k@ c #994C1A", "l@ c #964E1B", "m@ c #8D561C", "n@ c #845C1E", "o@ c #7A6521", "p@ c #776522", "q@ c #746823", "r@ c #726A23", "s@ c #706B24", "t@ c #6D6D25", "u@ c #91521C", "v@ c #8D561D", "w@ c #825E1F", "x@ c #7D6121", "y@ c #786522", "z@ c #766622", "A@ c #746822", "B@ c #706C24", "C@ c #6E6D24", "D@ c #6C6F25", "E@ c #6A7025", "F@ c #815E20", "G@ c #7F6020", "H@ c #7A6321", "I@ c #6F6D24", "J@ c #6D6E24", "K@ c #6A7026", "L@ c #687126", "M@ c #677326", "N@ c #8A581E", "O@ c #875A1D", "P@ c #855B1F", "Q@ c #7F6120", "R@ c #7D6221", "S@ c #786521", "T@ c #6F6C24", "U@ c #6C6E24", "V@ c #6A6F26", "W@ c #667226", "X@ c #647526", "Y@ c #627628", "Z@ c #8C571C", "`@ c #89581D", " # c #87591E", ".# c #796422", "+# c #766723", "@# c #746923", "## c #6F6C25", "$# c #687025", "%# c #657327", "&# c #637527", "*# c #617628", "=# c #5F7928", "-# c #875B1E", ";# c #845D1E", "># c #716B23", ",# c #6E6C24", "'# c #6D6D24", ")# c #6C6E25", "!# c #697026", "~# c #687226", "{# c #657326", "]# c #607728", "^# c #5F7828", "/# c #5D7928", "(# c #5B7B2A", "_# c #825D1F", ":# c #7B6221", "<# c #796421", "[# c #776722", "}# c #726A24", "|# c #6B6F25", "1# c #697025", "2# c #677126", "3# c #667326", "4# c #617627", "5# c #5B7A29", "6# c #597C29", "7# c #577E2A", "8# c #766623", "9# c #726924", "0# c #6C6F24", "a# c #677226", "b# c #647427", "c# c #627527", "d# c #5E7928", "e# c #5B7B28", "f# c #597C2A", "g# c #557F2A", "h# c #53812B", "i# c #7C6220", "j# c #6D6E25", "k# c #6B7026", "l# c #627627", "m# c #5E7929", "n# c #5B7B29", "o# c #577F2A", "p# c #54802A", "q# c #52822B", "r# c #4F832B", "s# c #6C6D24", "t# c #6B7025", "u# c #697126", "v# c #647527", "w# c #5D7A29", "x# c #587D2A", "y# c #567E2A", "z# c #54802B", "A# c #52812B", "B# c #50832C", "C# c #4E852C", "D# c #4C872D", "E# c #697125", "F# c #617728", "G# c #5F7929", "H# c #5D7929", "I# c #567F2A", "J# c #54812B", "K# c #50832B", "L# c #4F852C", "M# c #4D862D", "N# c #4B882D", "O# c #48892E", "P# c #6E6D25", "Q# c #6B6E25", "R# c #617727", "S# c #5C7A29", "T# c #5A7B29", "U# c #597D2A", "V# c #55802A", "W# c #51832C", "X# c #4F842C", "Y# c #4D852C", "Z# c #468B2E", "`# c #448C2F", " $ c #657426", ".$ c #5A7C29", "+$ c #577E29", "@$ c #55802B", "#$ c #51822C", "$$ c #4D862C", "%$ c #4A882D", "&$ c #48882D", "*$ c #478A2E", "=$ c #458C2F", "-$ c #438D2F", ";$ c #418E2F", ">$ c #647426", ",$ c #627528", "'$ c #577D2A", ")$ c #51822B", "!$ c #50842C", "~$ c #4A882E", "{$ c #458C2E", "]$ c #418F30", "^$ c #3E9130", "/$ c #3D9230", "($ c #607727", "_$ c #5C7A28", ":$ c #54812A", "<$ c #47892E", "[$ c #458B2F", "}$ c #3F9030", "|$ c #3C9331", "1$ c #399432", "2$ c #597D29", "3$ c #567F2B", "4$ c #4C862D", "5$ c #49882D", "6$ c #428E30", "7$ c #408F30", "8$ c #3D9131", "9$ c #3B9331", "0$ c #3A9431", "a$ c #379532", "b$ c #359832", " . + @ # $ % & * = - ; > , ' ) ! ~ { ] ^ / ( ", "_ : # < % [ } | 1 2 3 4 5 6 ! ~ 7 8 9 0 ( a b ", "c d % e f g h i 3 j k 6 l ~ { m n o p q r s t ", "u e v g w - x j k 6 ) ~ y m 8 z p A B C D E F ", "G g H I ; > J ' K L M N n O p q P Q R D F S T ", "= H U > J ' K V W X ] 9 / ( Y r Z D ` ...+.@.", "#.> , ' 6 $.~ %.&.^ 0 ( Y *.=.-.;.>.,.'.).!.~.", "4 5 6 $.{.].] ^ ^.( A /.C (.;._.:.<.).[.~.}.|.", "1.! 2.7 X 3.4.5.q B 6.7.E _.8.'.9.0.a.b.|.c.d.", "L y { n 4.e.f.g.C (.-.h.S i.+.j.a.k.|.c.l.m.n.", "X n O p ( /.o.p.-.q.r.T 9.s.t.k.u.v.w.x.n.y.z.", "^ 0 ( /.*.A.B.` S i.C.D.[.E.F.G.w.H.I.J.K.L.M.", "N.P *.p.R E O.:.'.P.[.~.Q.v.R.H.S.T.U.V.W.X.Y.", "/.s (.;._.:.'.s.[.Z.u.`.c.d. +T.U..+++X.Y.@+#+", "7.h._.8.'.).s.$+b.|.%+&+ +*+J.K.=+-+;+>+,+'+)+", "` .T 9.0.!+~+G.c.&+{+n.]+U.L.^+/+(+_+'+:+<+[+", "T +.s.}+|+b.1+2+3+n.y.z.4+^+/+5+_+6+7+8+9+0+a+", "s.[.~.Q.v.2+H.*+y.b+c+d+e+Y.@+f+g+h+i+j+a+k+l+", "~.m+n+o+3+ +p+q+4+r+s+t+u+v+7+<+w+x+y+z+A+B+C+", "G.c.D+ +E+F+G+W.e+H+I+_+f+7+<+J+K+L+M+l+C+N+O+", "D+ +P+J.4+L.s+/+(+Q+R+7+S+x+y+z+M+T+U+V+W+X+Y+", "S.Z+z.V.^+/+`+_+ @)+.@J++@a+k+@@#@$@W+%@Y+&@*@", "U.=+^+/+5+_+=@g+8+x++@a+k+@@#@V+W+X+-@;@>@,@'@", "W.X.)@_+6+!@~@9++@a+{@@@]@U+N+O+^@;@/@(@_@:@<@", "Y.I+6+g+8+x+[@y+z+M+}@|@N+1@2@;@3@,@4@5@6@7@8@", ",+=@)+<+x+9@z+A+]@$@0@a@1@Y+b@c@d@e@f@g@h@i@j@", "7+k@l@9@L+A+@@m@N+W+%@n@&@c@d@:@o@p@h@q@r@s@t@", "9+K+a+u@@@v@V+a@%@Y+w@*@,@x@6@y@z@A@r@B@C@D@E@", "a+k+@@#@$@W+1@2@;@F@G@'@H@y@h@q@r@s@I@J@K@L@M@", "A+l+$@N@O@P@;@/@Q@R@:@S@7@8@i@j@T@U@V@L@W@X@Y@", "Z@`@ #^@;@F@c@x@:@.#g@+#@#j@##t@K@$#W@%#&#*#=#", "W+-#;#w@c@4@:@6@g@h@i@>#,#'#)#!#~#{#&#]#^#/#(#", ";#_#c@,@:#<#7@[#A@}#s@'#|#1#2#3##^#/#5#6#7#", "c@G@x@H@y@8#A@9#s@'#0#1#a#3#b#c#]#d#e#f#7#g#h#", "i#H@<#7@q@r@B@T@j#k#L@a#b#l#]#m#n#f#7#o#p#q#r#", "<@y@+#i@>#T@s#t#u#3#v#l#]#^#w#n#x#y#z#A#B#C#D#", "8@i@j@T@j#E@E#~#{#&#F#G#H#n#x#I#J#q#K#L#M#N#O#", "j@B@P#Q#1#2#{#&#R#^#S#T#U#7#V#h#W#X#Y#N#O#Z#`#", "C@)#!#a# $&#F#]#d#S#.$+$@$h##$X#$$%$&$*$=$-$;$", "!#L@W@>$,$]#d#5#.$'$g#h#)$!$C#D#~$*${$-$]$^$/$", "3#v#c#($m#_$U#U#o#:$q#B#C#D#%$<$[$-$;$}$^$|$1$", "l#*#^#S#n#2$3$p#A#X#C#4$5$O#Z#`#6$7$8$9$0$a$b$"}; SDL2_image-2.8.8/test/svg-class.bmp0000664000000000000000000006461214516225512013724 0ustar00BMŠizlRR iџџџџ niW 3`fp™™™ffK(bГхџџџџџџџџџџџџџџа‘G!xСџџџ џ333џ```џfffџpppџ™™™џ™™™џ™™™џџfffџfffџKKKџ(((џџџџџџюЇR ‚зџџџbbbџГГГџхххџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџаааџ‘‘‘џGGGџџџџџљДV w№џ!!!џxxxџСССџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџяяя§ЇЇЇџRRRџ џџџџЫ?bфџ‚‚‚џзззџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџЭџџџЊџџџ™џџџ’џџџfџџџfџџџfџџџsџџџ™џџџ™џџџОџџџтџџџџџџџџџџџџџџџџџџџџџџџџџџџџњњњ§ДДДџVVVџџџџЕ-Kд џwwwџ№№№џџџџџџџџџџџџџџџџџџџџџџџџџџџџКџџџrџџџ%џџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџDџџџŒџџџсџџџџџџџџџџџџџџџџџџџџџџџџЭЭЭќ???џџџўЖџbbbџфффџџџџџџџџџџџџџџџџџџџџєџџџЏџџџ\џџџџџџџџџџџџџџџџџџџџџџџџџџџ-џџџ~џџџЮџџџџџџџџџџџџџџџџџџџџЕЕЕў---џџџіf$йKKKџдддџџџџџџџџџџџџџџџџџџџџеџџџTџџџџџџџџџџџџџџџџџџџџџџџџџџџ†џџџѕџџџџџџџџџџџџџџџўџџџџ”DяЖЖЖџџџџџџџџџџџџџџџџџџџџцџџџdџџџџџџџџџџџџџџџџџџџџџ›џџџ§џџџџџџџџџџџџїїї§fffџџџМn$$$ћйййџџџџџџџџџџџџџџџџёџџџyџџџ џџџџџџџџџџџџџџџ*џџџБџџџџџџџџџџџџџџџџ”””џџџл'šDDDџяяяџџџџџџџџџџџџџџџџ’џџџџџџџџџџџџџџџџџџ;џџџЯџџџџџџџџџџџџМММўџџёHЌnnnџћћћџџџџџџџџџџџџєџџџQџџџџџџџџџџџџџџџЅџџџџџџџџџџџџммм§'''џџќI ‚šššџџџџџџџџџџџџџџџџсџџџ/џџџџџџџџџџџџxџџџўџџџџџџџџђђђ§HHHџџ№(SЌЌЌџџџџџџџџџџџџџџџџФџџџџџџџџџџџџMџџџѓџџџџџџџџќќќўIIIџџк0‚‚‚љџџџџџџџџџџџџ§§§Ÿџџџџџџџџџџџџ,џџџпџџџџџџџџ№№№ў(((џџЛZZZыџџџџџџџџџџџџєєє“јјјџџџџџџџџџџџџнџџџџџџџџкккўџџ’;;;ЯѕѕѕџџџџџџџџџёёёПџџџџџџџџџџџџ,џџџђџџџџџџџџЛЛЛџџџf777+++‡тттџџџџџџџџџѓѓѓрџџџџџџџџџџџџNџџџ§џџџџџџџџ’’’џџїDDDDDDШШШћџџџџџџџџїїїєџџџ$џџџџџџџџџxџџџџџџџџџџџџfffџџ žžžЧџџџџџџџџћћћўћћћC§§§џџџџџџЅџџџџџџџџїїїџџ§.dddddd3ёёёўџџџџџџџџЛЛЛ–лллџџџџџџџџџЬџџџџџџџџ   џџЕЌЌЌиџџџџџџџџлллђџџџџџџџџџџџџ8џџџџџџџџ§§§ў...џџ@ooooooNњњњўџџџџџџџџЇЇЇlгггџџџџџџЎџџџџџџџџЕЕЕџџЫЗЗЗИИИцџџџџџџџџааачЛЛЛџџџџџџ(џџџћџџџџџџџџ@@@џџTЗЗЗЗЗЗGџџџўџџџџџџџўЇЇЇOЇЇЇџџџџџџ—џџџџџџџџЫЫЫџџБюююнннжџџџџџџџџИИИхЏЏЏџџџџџџџџџєџџџџџџџџTTTџі џџџџџџ§§§ўџџџџњњњџFFF{FFFџџџџџџƒџџџџџџџџБББџџRььььььpџџџџџџџџЗЗЗўџџџџџџџџџџџџџџіііџ џЁ№№№сссаџџџџџџџџvvvн;;;џџџџџџЭџџџџџџџџRRRџэџџџџџџњњњќџџџџ§§§џ000„000џџџџџџ}џџџџџџџџЁЁЁџџBѓѓѓѓѓѓ\џџџџџџџџЧЧЧџ'џџџџџџ-џџџџџџџџэээџџ‘ёёёуууМџџџџџџџџ‚‚‚щAAAџџџџџџнџџџџџџџџBBBџеџџџџџџљљљѕџџџџџџџџ888Џ888џџџџџџџџџџџџџџ‘‘‘џјџџџџџџ;џџџџџџџџзззџyџџџџџџ=џџџџџџџџеееџџ#џџџџџџcџџџџџџџџЁЁЁџTџџџџџџ џџџ§џџџџјјјџџJџџџџџџ‰џџџџџџџџyyyџ/џџџџџџрџџџџџџџџ###џgџџџџџџАџџџџџџџџTTTџџџџџџџЙџџџџџџџџJJJџtџџџџџџЮџџџџџџџџ///џџџџџџџ•џџџџџџџџgggџџџџџџџлџџџџџџџџџџџџџџџ†џџџџџџџџtttџџџџџџџчџџџџџџџџџџџџџџџyџџџџџџџџџ’џџџџџџєџџџџџџџџџџџџџџџmџџџџџџџџџ†џџџџџџіџџџџџџџџџџџџџџџjџџџџџџџџ’’’џyџџџџџџъџџџџџџџџџ)џџџџџџwџџџџџџџџ†††џmџџџџџџнџџџџџџџџџEџџџџџџƒџџџџџџџџyyyџVџџџџџџбџџџџџџџџ)))џmџџџџџџџџџџџџџџmmmџ1џџџџџџМџџџџџџџџEEEџ’џџџџџџЋџџџџџџџџVVVў џџџџџџ—џџџџџџџџmmmџНћћћїїїиџџџџџџџџ666чџџџџџџoџџџџџџџџ’’’џј џџџџџџћћћњџџџџўўўџЕџџџџџџJџџџџџџџџНННџџUџџџџџџ#џџџџџџџџтттџdџџџџџџџџџўџџџџјјјџ џЅыыыыыыtџџџџџџџџВВВўџџџџџџЩџџџџџџџџUUUџ№№№№сссгџџџџџџџџtttк:::џџџџџџzџџџџџџџџЅЅЅџџEџџџџџџћћћ§џџџџ§§§џ,,,,,,џџџџџџ*џџџџџџџџ№№№џџ•ёёёёёё`џџџџџџџџУУУџ$џџџџџџйџџџџџџџџEEEџцггглллЧџџџџџџџџц???џџџџџџŠџџџџџџџџ•••џџrЬЬЬЬЬЬ єєє§џџџџџџџџ???‘???џџџџџџ:џџџџџџџџцццџџюggg—џџџџџџџџеееќџџџџџџџџџфџџџџџџџџrrrџџˆ?????? йййњџџџџџџџџЏЏЏЙWWWџџџџџџeџџџџџџџџюююџџіvvv‘‘‘­џџџџџџџџ№№№§ДДДДДДџџџџџџџџџзџџџџџџџџˆˆˆџџž[[[[[[ццц§џџџџџџџџАААЈВВВџџџџџџQџџџџџџџџіііџџќ/gggœœœХџџџџџџџџчччљЬЬЬ ЬЬЬџџџџџџХџџџџџџџџžžžџџй333^№№№ўџџџџџџџџЌЌЌŽМММџџџџџџ;џџџџџџџџќќќў///џџЙ/“““њџџџџџџџџнннёџџџџџџџџџџџџАџџџџџџџџйййўџџ‘YYYъўўўџџџџџџџџџрррTяяяџџџџџџ'џџџѓџџџџџџџџЙЙЙџџџd:::Юєєєџџџџџџџџџ№№№ОшшшџџџџџџNџџџўџџџџџџџџ‘‘‘џџљ>!!!Ісссџџџџџџџџџѓѓѓсџџџџџџџџџџџџzџџџџџџџџџџџџdddџџь> ‘ФФФџџџџџџџџџїїїѕџџџ%џџџџџџџџџІџџџџџџџџљљљў>>>џџљeЗžžžџџџџџџџџџќќќ§џџџDџџџџџџџџџџџџЫџџџџџџџџэээ§>>>џџџ‘$зџџџџџџџџџџџџџњњњoќќќџџџџџџџџџцџџџџџџџџњњњ§eeeџџџКCюЗЗЗџџџџџџџџџџџџџџџџšќќќџџџџџџ1џџџхџџџџџџџџџџџџ‘‘‘џџџл+q$$$ћзззџџџџџџџџџџџџџџџџЊџџџџџџџџџџџџџџџЪџџџџџџџџџџџџКККџџџўJвCCCџюююџџџџџџџџџџџџџџџџџџџџџџџџџџџџ џџџІџџџџџџџџџџџџммм§+++џџџіˆ4РџqqqџћћћџџџџџџџџџџџџѕџџџSџџџџџџџџџџџџzџџџџџџџџџџџџџџџўџџџџюo#ЈџJJJџвввџџџџџџџџџџџџџџџџтџџџ1џџџџџџџџџNџџџєџџџџџџџџџџџџїїї§ˆˆˆџџџџц–IgЕњ444џРРРџџџџџџџџџџџџџџџџџџџџХџџџџџџџџџџџџ'џџџВџџџџџџџџџџџџџџџџ№№№ќoooџџџџџёЋT%{Цџџ###џЈЈЈџџџџџџџџџџџџџџџџџџџџёџџџ{џџџџџџџџџџџџџџџ<џџџЪџџџџџџџџџџџџџџџџшшшќ–––џIIIџџџџџћО™gD3, 33X|ŸнџџџgggџЕЕЕџњњњџџџџџџџџџџџџџџџџљџџџ“џџџџџџџџџџџџџџџџџџUџџџкџџџџџџџџџџџџџџџџџџџџђђђ§ЋЋЋџTTTџџџџџџџџџџџџџџџџџџ%%%џ{{{џЦЦЦџџџџџџџџџџџџџџџџџџџџџџџџџџџџІџџџ#џџџџџџџџџџџџџџџiџџџшџџџџџџџџџџџџџџџџџџџџџџџџћћћўОООџ™™™џgggџDDDџ333џ,,,џџџџ џ333џ333џXXXџ|||џŸŸŸџнннџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџМџџџ3џџџџџџџџџџџџџџџџџџ?џџџ‡џџџоџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџ§џџџИџџџmџџџџџџџџџџџџџџџџџџџџџ)џџџ|џџџЩџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџџђџџџ­џџџXџџџџџџџџџџџџџџџџџџџџџџџџџџџLџџџoџџџ™џџџЦџџџЬџџџжџџџџџџџџџџџџџџџѕџџџЬџџџЬџџџБџџџŽџџџfџџџ7џџџџџџџџџџџџSDL2_image-2.8.8/test/svg-class.svg0000664000000000000000000000276414244303504013741 0ustar00 ]> SDL2_image-2.8.8/test/svg.bmp0000664000000000000000000001017214241727434012616 0ustar00BMzzl  џџџџ niWџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџSDL2_image-2.8.8/test/svg.svg0000664000000000000000000000103414241727434012634 0ustar00 SDL2_image-2.8.8/test/svg64.bmp0000664000000000000000000004017214241727434012773 0ustar00BMz@zl@@ @џџџџ niWџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџџ™fџSDL2_image-2.8.8/test/template.test.in0000664000000000000000000000006314241727434014436 0ustar00[Test] Type=session Exec=@installedtestsdir@/@exe@ SDL2_image-2.8.8/REVISION.txt0000644000000000000000000000003214761421756012334 0ustar00release-2.8.8-0-gc1bf2245 SDL2_image-2.8.8/.git-hash0000644000000000000000000000005114761421756012043 0ustar00c1bf2245b0ba63a25afe2f8574d305feca25af77