Progressia/lib/stb/include/stb/stb_image.h
OLEGSHA a110c9de03
Added logging, refactored versioning; STB is now included
- Added logging
- Rewrote versioning code
- Refactored dependency management
  - STB (stb_image.h) is now included
  - All other dependencies now use find_package
- Cross-compilation from Linux to Windows is now possible
2022-10-31 21:12:48 +03:00

2704 lines
93 KiB
C++

/*
* This is a stripped-down version of stb_image.h as published at
*
* https://github.com/nothings/stb/blob/8b5f1f37b5b75829fc72d38e7b5d4bcbf8a26d55/stb_image.h
*
* The changes include the removal of code not necessary for PNG processing and
* comment removal. See original file for documentation, version history, etc.
* This was done to reduce filesize in repository.
*
* - OLEGSHA
*/
/* stb_image - v2.27 - public domain image loader - http://nothings.org/stb
no warranty implied; use at your own risk
LICENSE
See end of file for license information.
============================ Contributors =========================
Image formats Extensions, features
Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info)
Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info)
Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG)
Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks)
Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG)
Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip)
Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD)
github:urraka (animated gif) Junggon Kim (PNM comments)
Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA)
socks-the-fox (16-bit PNG)
Jeremy Sawicki (handle all ImageNet JPGs)
Optimizations & bugfixes Mikhail Morozov (1-bit BMP)
Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query)
Arseny Kapoulkine Simon Breuss (16-bit PNM)
John-Mark Allen
Carmelo J Fdez-Aguera
Bug & warning fixes
Marc LeBlanc David Woo Guillaume George Martins Mozeiko
Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski
Phil Jordan Dave Moore Roy Eltham
Hayaki Saito Nathan Reed Won Chun
Luke Graham Johan Duparc Nick Verigakis the Horde3D community
Thomas Ruf Ronny Chevalier github:rlyeh
Janez Zemva John Bartholomew Michal Cichon github:romigrou
Jonathan Blow Ken Hamada Tero Hanninen github:svdijk
Eugene Golushkov Laurent Gomila Cort Stratton github:snagar
Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex
Cass Everitt Ryamond Barbiero github:grim210
Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw
Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus
Josh Tobin Matthew Gregan github:poppolopoppo
Julian Raschke Gregory Mullen Christian Floisand github:darealshinji
Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007
Brad Weinberger Matvey Cherevko github:mosra
Luca Sas Alexander Veselov Zack Middleton [reserved]
Ryan C. Gordon [reserved] [reserved]
DO NOT ADD YOUR NAME HERE
Jacko Dirks
To add your name to the credits, pick a random blank space in the middle and fill it.
80% of merge conflicts on stb PRs are due to people adding their name at the end
of the credits.
*/
#ifndef STBI_INCLUDE_STB_IMAGE_H
#define STBI_INCLUDE_STB_IMAGE_H
#ifndef STBI_NO_STDIO
#include <stdio.h>
#endif // STBI_NO_STDIO
#define STBI_VERSION 1
enum
{
STBI_default = 0, // only used for desired_channels
STBI_grey = 1,
STBI_grey_alpha = 2,
STBI_rgb = 3,
STBI_rgb_alpha = 4
};
#include <stdlib.h>
typedef unsigned char stbi_uc;
typedef unsigned short stbi_us;
#ifdef __cplusplus
extern "C" {
#endif
#ifndef STBIDEF
#ifdef STB_IMAGE_STATIC
#define STBIDEF static
#else
#define STBIDEF extern
#endif
#endif
//////////////////////////////////////////////////////////////////////////////
//
// PRIMARY API - works on images of any type
//
//
// load image by filename, open file, or memory buffer
//
typedef struct
{
int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read
void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative
int (*eof) (void *user); // returns nonzero if we are at end of file/data
} stbi_io_callbacks;
////////////////////////////////////
//
// 8-bits-per-channel interface
//
STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels);
#ifndef STBI_NO_STDIO
STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
// for stbi_load_from_file, file pointer is left pointing immediately after image
#endif
#ifdef STBI_WINDOWS_UTF8
STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);
#endif
////////////////////////////////////
//
// 16-bits-per-channel interface
//
STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
#ifndef STBI_NO_STDIO
STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
#endif
////////////////////////////////////
//
// float-per-channel interface
//
#ifndef STBI_NO_LINEAR
STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);
#ifndef STBI_NO_STDIO
STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);
STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);
#endif
#endif
#ifndef STBI_NO_LINEAR
STBIDEF void stbi_ldr_to_hdr_gamma(float gamma);
STBIDEF void stbi_ldr_to_hdr_scale(float scale);
#endif // STBI_NO_LINEAR
// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR
STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);
STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);
#ifndef STBI_NO_STDIO
STBIDEF int stbi_is_hdr (char const *filename);
STBIDEF int stbi_is_hdr_from_file(FILE *f);
#endif // STBI_NO_STDIO
// get a VERY brief reason for failure
// on most compilers (and ALL modern mainstream compilers) this is threadsafe
STBIDEF const char *stbi_failure_reason (void);
// free the loaded image -- this is just free()
STBIDEF void stbi_image_free (void *retval_from_stbi_load);
// get image dimensions & components without fully decoding
STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);
STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp);
STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len);
STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user);
#ifndef STBI_NO_STDIO
STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp);
STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp);
STBIDEF int stbi_is_16_bit (char const *filename);
STBIDEF int stbi_is_16_bit_from_file(FILE *f);
#endif
// for image formats that explicitly notate that they have premultiplied alpha,
// we just return the colors as stored in the file. set this flag to force
// unpremultiplication. results are undefined if the unpremultiply overflow.
STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply);
// indicate whether we should process iphone images back to canonical format,
// or just pass them through "as-is"
STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert);
// flip the image vertically, so the first pixel in the output array is the bottom left
STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip);
// as above, but only applies to images loaded on the thread that calls the function
// this function is only available if your compiler supports thread-local variables;
// calling it will fail to link if your compiler doesn't
STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply);
STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert);
STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip);
// ZLIB client - used by PNG, available for other purposes
STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);
STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);
STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);
STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);
STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);
#ifdef __cplusplus
}
#endif
//
//
//// end header file /////////////////////////////////////////////////////
#endif // STBI_INCLUDE_STB_IMAGE_H
#ifdef STB_IMAGE_IMPLEMENTATION
#include <stdarg.h>
#include <stddef.h> // ptrdiff_t on osx
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)
#include <math.h> // ldexp, pow
#endif
#ifndef STBI_NO_STDIO
#include <stdio.h>
#endif
#ifndef STBI_ASSERT
#include <assert.h>
#define STBI_ASSERT(x) assert(x)
#endif
#ifdef __cplusplus
#define STBI_EXTERN extern "C"
#else
#define STBI_EXTERN extern
#endif
#ifndef _MSC_VER
#ifdef __cplusplus
#define stbi_inline inline
#else
#define stbi_inline
#endif
#else
#define stbi_inline __forceinline
#endif
#ifndef STBI_NO_THREAD_LOCALS
#if defined(__cplusplus) && __cplusplus >= 201103L
#define STBI_THREAD_LOCAL thread_local
#elif defined(__GNUC__) && __GNUC__ < 5
#define STBI_THREAD_LOCAL __thread
#elif defined(_MSC_VER)
#define STBI_THREAD_LOCAL __declspec(thread)
#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
#define STBI_THREAD_LOCAL _Thread_local
#endif
#ifndef STBI_THREAD_LOCAL
#if defined(__GNUC__)
#define STBI_THREAD_LOCAL __thread
#endif
#endif
#endif
#ifdef _MSC_VER
typedef unsigned short stbi__uint16;
typedef signed short stbi__int16;
typedef unsigned int stbi__uint32;
typedef signed int stbi__int32;
#else
#include <stdint.h>
typedef uint16_t stbi__uint16;
typedef int16_t stbi__int16;
typedef uint32_t stbi__uint32;
typedef int32_t stbi__int32;
#endif
// should produce compiler error if size is wrong
typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1];
#ifdef _MSC_VER
#define STBI_NOTUSED(v) (void)(v)
#else
#define STBI_NOTUSED(v) (void)sizeof(v)
#endif
#ifdef _MSC_VER
#define STBI_HAS_LROTL
#endif
#ifdef STBI_HAS_LROTL
#define stbi_lrot(x,y) _lrotl(x,y)
#else
#define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31)))
#endif
#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED))
// ok
#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED)
// ok
#else
#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)."
#endif
#ifndef STBI_MALLOC
#define STBI_MALLOC(sz) malloc(sz)
#define STBI_REALLOC(p,newsz) realloc(p,newsz)
#define STBI_FREE(p) free(p)
#endif
#ifndef STBI_REALLOC_SIZED
#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz)
#endif
// x86/x64 detection
#if defined(__x86_64__) || defined(_M_X64)
#define STBI__X64_TARGET
#elif defined(__i386) || defined(_M_IX86)
#define STBI__X86_TARGET
#endif
#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)
// gcc doesn't support sse2 intrinsics unless you compile with -msse2,
// which in turn means it gets to use SSE2 everywhere. This is unfortunate,
// but previous attempts to provide the SSE2 functions with runtime
// detection caused numerous issues. The way architecture extensions are
// exposed in GCC/Clang is, sadly, not really suited for one-file libs.
// New behavior: if compiled with -msse2, we use SSE2 without any
// detection; if not, we don't use it at all.
#define STBI_NO_SIMD
#endif
#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD)
// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET
//
// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the
// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant.
// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not
// simultaneously enabling "-mstackrealign".
//
// See https://github.com/nothings/stb/issues/81 for more information.
//
// So default to no SSE2 on 32-bit MinGW. If you've read this far and added
// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2.
#define STBI_NO_SIMD
#endif
#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET))
#define STBI_SSE2
#include <emmintrin.h>
#ifdef _MSC_VER
#if _MSC_VER >= 1400 // not VC6
#include <intrin.h> // __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
#else // assume GCC-style if not VC++
#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
#endif
#endif
// ARM NEON
#if defined(STBI_NO_SIMD) && defined(STBI_NEON)
#undef STBI_NEON
#endif
#ifdef STBI_NEON
#include <arm_neon.h>
#ifdef _MSC_VER
#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name
#else
#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))
#endif
#endif
#ifndef STBI_SIMD_ALIGN
#define STBI_SIMD_ALIGN(type, name) type name
#endif
#ifndef STBI_MAX_DIMENSIONS
#define STBI_MAX_DIMENSIONS (1 << 24)
#endif
///////////////////////////////////////////////
//
// stbi__context struct and start_xxx functions
// stbi__context structure is our basic context used by all images, so it
// contains all the IO context, plus some basic image information
typedef struct
{
stbi__uint32 img_x, img_y;
int img_n, img_out_n;
stbi_io_callbacks io;
void *io_user_data;
int read_from_callbacks;
int buflen;
stbi_uc buffer_start[128];
int callback_already_read;
stbi_uc *img_buffer, *img_buffer_end;
stbi_uc *img_buffer_original, *img_buffer_original_end;
} stbi__context;
static void stbi__refill_buffer(stbi__context *s);
// initialize a memory-decode context
static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len)
{
s->io.read = NULL;
s->read_from_callbacks = 0;
s->callback_already_read = 0;
s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer;
s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len;
}
// initialize a callback-based context
static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user)
{
s->io = *c;
s->io_user_data = user;
s->buflen = sizeof(s->buffer_start);
s->read_from_callbacks = 1;
s->callback_already_read = 0;
s->img_buffer = s->img_buffer_original = s->buffer_start;
stbi__refill_buffer(s);
s->img_buffer_original_end = s->img_buffer_end;
}
#ifndef STBI_NO_STDIO
static int stbi__stdio_read(void *user, char *data, int size)
{
return (int) fread(data,1,size,(FILE*) user);
}
static void stbi__stdio_skip(void *user, int n)
{
int ch;
fseek((FILE*) user, n, SEEK_CUR);
ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */
if (ch != EOF) {
ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */
}
}
static int stbi__stdio_eof(void *user)
{
return feof((FILE*) user) || ferror((FILE *) user);
}
static stbi_io_callbacks stbi__stdio_callbacks =
{
stbi__stdio_read,
stbi__stdio_skip,
stbi__stdio_eof,
};
static void stbi__start_file(stbi__context *s, FILE *f)
{
stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f);
}
//static void stop_file(stbi__context *s) { }
#endif // !STBI_NO_STDIO
static void stbi__rewind(stbi__context *s)
{
// conceptually rewind SHOULD rewind to the beginning of the stream,
// but we just rewind to the beginning of the initial buffer, because
// we only use it after doing 'test', which only ever looks at at most 92 bytes
s->img_buffer = s->img_buffer_original;
s->img_buffer_end = s->img_buffer_original_end;
}
enum
{
STBI_ORDER_RGB,
STBI_ORDER_BGR
};
typedef struct
{
int bits_per_channel;
int num_channels;
int channel_order;
} stbi__result_info;
static int stbi__png_test(stbi__context *s);
static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri);
static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp);
static int stbi__png_is16(stbi__context *s);
static
#ifdef STBI_THREAD_LOCAL
STBI_THREAD_LOCAL
#endif
const char *stbi__g_failure_reason;
STBIDEF const char *stbi_failure_reason(void)
{
return stbi__g_failure_reason;
}
#ifndef STBI_NO_FAILURE_STRINGS
static int stbi__err(const char *str)
{
stbi__g_failure_reason = str;
return 0;
}
#endif
static void *stbi__malloc(size_t size)
{
return STBI_MALLOC(size);
}
// stb_image uses ints pervasively, including for offset calculations.
// therefore the largest decoded image size we can support with the
// current code, even on 64-bit targets, is INT_MAX. this is not a
// significant limitation for the intended use case.
//
// we do, however, need to make sure our size calculations don't
// overflow. hence a few helper functions for size calculations that
// multiply integers together, making sure that they're non-negative
// and no overflow occurs.
// return 1 if the sum is valid, 0 on overflow.
// negative terms are considered invalid.
static int stbi__addsizes_valid(int a, int b)
{
if (b < 0) return 0;
// now 0 <= b <= INT_MAX, hence also
// 0 <= INT_MAX - b <= INTMAX.
// And "a + b <= INT_MAX" (which might overflow) is the
// same as a <= INT_MAX - b (no overflow)
return a <= INT_MAX - b;
}
// returns 1 if the product is valid, 0 on overflow.
// negative factors are considered invalid.
static int stbi__mul2sizes_valid(int a, int b)
{
if (a < 0 || b < 0) return 0;
if (b == 0) return 1; // mul-by-0 is always safe
// portable way to check for no overflows in a*b
return a <= INT_MAX/b;
}
// 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);
}
// 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
// mallocs with size overflow checking
static void *stbi__malloc_mad2(int a, int b, int add)
{
if (!stbi__mad2sizes_valid(a, b, add)) return NULL;
return stbi__malloc(a*b + add);
}
static void *stbi__malloc_mad3(int a, int b, int c, int add)
{
if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL;
return stbi__malloc(a*b*c + add);
}
#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM)
static void *stbi__malloc_mad4(int a, int b, int c, int d, int add)
{
if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL;
return stbi__malloc(a*b*c*d + add);
}
#endif
// stbi__err - error
// stbi__errpf - error returning pointer to float
// stbi__errpuc - error returning pointer to unsigned char
#ifdef STBI_NO_FAILURE_STRINGS
#define stbi__err(x,y) 0
#elif defined(STBI_FAILURE_USERMSG)
#define stbi__err(x,y) stbi__err(y)
#else
#define stbi__err(x,y) stbi__err(x)
#endif
#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL))
#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL))
STBIDEF void stbi_image_free(void *retval_from_stbi_load)
{
STBI_FREE(retval_from_stbi_load);
}
#ifndef STBI_NO_LINEAR
static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp);
#endif
static int stbi__vertically_flip_on_load_global = 0;
STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)
{
stbi__vertically_flip_on_load_global = flag_true_if_should_flip;
}
#ifndef STBI_THREAD_LOCAL
#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global
#else
static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set;
STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip)
{
stbi__vertically_flip_on_load_local = flag_true_if_should_flip;
stbi__vertically_flip_on_load_set = 1;
}
#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \
? stbi__vertically_flip_on_load_local \
: stbi__vertically_flip_on_load_global)
#endif // STBI_THREAD_LOCAL
static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc)
{
memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields
ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed
ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order
ri->num_channels = 0;
// test the formats with a very explicit header first (at least a FOURCC
// or distinctive magic number first)
if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri);
STBI_NOTUSED(bpc);
// 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
return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt");
}
static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels)
{
int i;
int img_len = w * h * channels;
stbi_uc *reduced;
reduced = (stbi_uc *) stbi__malloc(img_len);
if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory");
for (i = 0; i < img_len; ++i)
reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling
STBI_FREE(orig);
return reduced;
}
static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels)
{
int i;
int img_len = w * h * channels;
stbi__uint16 *enlarged;
enlarged = (stbi__uint16 *) stbi__malloc(img_len*2);
if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory");
for (i = 0; i < img_len; ++i)
enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff
STBI_FREE(orig);
return enlarged;
}
static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel)
{
int row;
size_t bytes_per_row = (size_t)w * bytes_per_pixel;
stbi_uc temp[2048];
stbi_uc *bytes = (stbi_uc *)image;
for (row = 0; row < (h>>1); row++) {
stbi_uc *row0 = bytes + row*bytes_per_row;
stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row;
// swap row0 with row1
size_t bytes_left = bytes_per_row;
while (bytes_left) {
size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp);
memcpy(temp, row0, bytes_copy);
memcpy(row0, row1, bytes_copy);
memcpy(row1, temp, bytes_copy);
row0 += bytes_copy;
row1 += bytes_copy;
bytes_left -= bytes_copy;
}
}
}
static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)
{
stbi__result_info ri;
void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8);
if (result == NULL)
return NULL;
// it is the responsibility of the loaders to make sure we get either 8 or 16 bit.
STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16);
if (ri.bits_per_channel != 8) {
result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
ri.bits_per_channel = 8;
}
// @TODO: move stbi__convert_format to here
if (stbi__vertically_flip_on_load) {
int channels = req_comp ? req_comp : *comp;
stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc));
}
return (unsigned char *) result;
}
static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp)
{
stbi__result_info ri;
void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16);
if (result == NULL)
return NULL;
// it is the responsibility of the loaders to make sure we get either 8 or 16 bit.
STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16);
if (ri.bits_per_channel != 16) {
result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp);
ri.bits_per_channel = 16;
}
// @TODO: move stbi__convert_format16 to here
// @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision
if (stbi__vertically_flip_on_load) {
int channels = req_comp ? req_comp : *comp;
stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16));
}
return (stbi__uint16 *) result;
}
#ifndef STBI_NO_STDIO
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide);
STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default);
#endif
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input)
{
return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL);
}
#endif
static FILE *stbi__fopen(char const *filename, char const *mode)
{
FILE *f;
#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8)
wchar_t wMode[64];
wchar_t wFilename[1024];
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename)))
return 0;
if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode)))
return 0;
#if defined(_MSC_VER) && _MSC_VER >= 1400
if (0 != _wfopen_s(&f, wFilename, wMode))
f = 0;
#else
f = _wfopen(wFilename, wMode);
#endif
#elif defined(_MSC_VER) && _MSC_VER >= 1400
if (0 != fopen_s(&f, filename, mode))
f=0;
#else
f = fopen(filename, mode);
#endif
return f;
}
STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp)
{
FILE *f = stbi__fopen(filename, "rb");
unsigned char *result;
if (!f) return stbi__errpuc("can't fopen", "Unable to open file");
result = stbi_load_from_file(f,x,y,comp,req_comp);
fclose(f);
return result;
}
STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
{
unsigned char *result;
stbi__context s;
stbi__start_file(&s,f);
result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
if (result) {
// need to 'unget' all the characters in the IO buffer
fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
}
return result;
}
STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp)
{
stbi__uint16 *result;
stbi__context s;
stbi__start_file(&s,f);
result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp);
if (result) {
// need to 'unget' all the characters in the IO buffer
fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR);
}
return result;
}
STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp)
{
FILE *f = stbi__fopen(filename, "rb");
stbi__uint16 *result;
if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file");
result = stbi_load_from_file_16(f,x,y,comp,req_comp);
fclose(f);
return result;
}
#endif //!STBI_NO_STDIO
STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels)
{
stbi__context s;
stbi__start_mem(&s,buffer,len);
return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);
}
STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels)
{
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user);
return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels);
}
STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
{
stbi__context s;
stbi__start_mem(&s,buffer,len);
return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
}
STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
{
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp);
}
#ifndef STBI_NO_LINEAR
static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)
{
unsigned char *data;
data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp);
if (data)
return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);
return stbi__errpf("unknown image type", "Image not of any known type, or corrupt");
}
STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)
{
stbi__context s;
stbi__start_mem(&s,buffer,len);
return stbi__loadf_main(&s,x,y,comp,req_comp);
}
STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)
{
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);
return stbi__loadf_main(&s,x,y,comp,req_comp);
}
#ifndef STBI_NO_STDIO
STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)
{
float *result;
FILE *f = stbi__fopen(filename, "rb");
if (!f) return stbi__errpf("can't fopen", "Unable to open file");
result = stbi_loadf_from_file(f,x,y,comp,req_comp);
fclose(f);
return result;
}
STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)
{
stbi__context s;
stbi__start_file(&s,f);
return stbi__loadf_main(&s,x,y,comp,req_comp);
}
#endif // !STBI_NO_STDIO
#endif // !STBI_NO_LINEAR
// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is
// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always
// reports false!
STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)
{
STBI_NOTUSED(buffer);
STBI_NOTUSED(len);
return 0;
}
#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)
{
STBI_NOTUSED(f);
return 0;
}
#endif // !STBI_NO_STDIO
STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user)
{
STBI_NOTUSED(clbk);
STBI_NOTUSED(user);
return 0;
}
#ifndef STBI_NO_LINEAR
static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f;
STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; }
STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; }
#endif
static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f;
STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; }
STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; }
//////////////////////////////////////////////////////////////////////////////
//
// Common code used by all image loaders
//
enum
{
STBI__SCAN_load=0,
STBI__SCAN_type,
STBI__SCAN_header
};
static void stbi__refill_buffer(stbi__context *s)
{
int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen);
s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original);
if (n == 0) {
// at end of file, treat same as if from memory, but need to handle case
// where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file
s->read_from_callbacks = 0;
s->img_buffer = s->buffer_start;
s->img_buffer_end = s->buffer_start+1;
*s->img_buffer = 0;
} else {
s->img_buffer = s->buffer_start;
s->img_buffer_end = s->buffer_start + n;
}
}
stbi_inline static stbi_uc stbi__get8(stbi__context *s)
{
if (s->img_buffer < s->img_buffer_end)
return *s->img_buffer++;
if (s->read_from_callbacks) {
stbi__refill_buffer(s);
return *s->img_buffer++;
}
return 0;
}
// nothing
static void stbi__skip(stbi__context *s, int n)
{
if (n == 0) return; // already there!
if (n < 0) {
s->img_buffer = s->img_buffer_end;
return;
}
if (s->io.read) {
int blen = (int) (s->img_buffer_end - s->img_buffer);
if (blen < n) {
s->img_buffer = s->img_buffer_end;
(s->io.skip)(s->io_user_data, n - blen);
return;
}
}
s->img_buffer += n;
}
static 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;
}
static int stbi__get16be(stbi__context *s)
{
int z = stbi__get8(s);
return (z << 8) + stbi__get8(s);
}
static stbi__uint32 stbi__get32be(stbi__context *s)
{
stbi__uint32 z = stbi__get16be(s);
return (z << 16) + stbi__get16be(s);
}
// nothing
#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings
//////////////////////////////////////////////////////////////////////////////
//
// 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);
}
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;
}
static stbi__uint16 stbi__compute_y_16(int r, int g, int b)
{
return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8);
}
static stbi__uint16 *stbi__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;
}
#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
//////////////////////////////////////////////////////////////////////////////
//
// "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
// 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
// fast-way is faster to check than jpeg huffman, but slow way is slower
#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables
#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1)
#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet
// zlib-style huffman encoding
// (jpegs packs from left, zlib from right, so can't share code)
typedef struct
{
stbi__uint16 fast[1 << STBI__ZFAST_BITS];
stbi__uint16 firstcode[16];
int maxcode[17];
stbi__uint16 firstsymbol[16];
stbi_uc size[STBI__ZNSYMS];
stbi__uint16 value[STBI__ZNSYMS];
} stbi__zhuffman;
stbi_inline static int stbi__bitreverse16(int n)
{
n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1);
n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2);
n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4);
n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8);
return n;
}
stbi_inline static int stbi__bit_reverse(int v, int bits)
{
STBI_ASSERT(bits <= 16);
// to bit reverse n bits, reverse 16 and shift
// e.g. 11 bits, bit reverse and shift away 5
return stbi__bitreverse16(v) >> (16-bits);
}
static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num)
{
int i,k=0;
int code, next_code[16], sizes[17];
// DEFLATE spec for generating codes
memset(sizes, 0, sizeof(sizes));
memset(z->fast, 0, sizeof(z->fast));
for (i=0; i < num; ++i)
++sizes[sizelist[i]];
sizes[0] = 0;
for (i=1; i < 16; ++i)
if (sizes[i] > (1 << i))
return stbi__err("bad sizes", "Corrupt PNG");
code = 0;
for (i=1; i < 16; ++i) {
next_code[i] = code;
z->firstcode[i] = (stbi__uint16) code;
z->firstsymbol[i] = (stbi__uint16) k;
code = (code + sizes[i]);
if (sizes[i])
if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG");
z->maxcode[i] = code << (16-i); // preshift for inner loop
code <<= 1;
k += sizes[i];
}
z->maxcode[16] = 0x10000; // sentinel
for (i=0; i < num; ++i) {
int s = sizelist[i];
if (s) {
int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s];
stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i);
z->size [c] = (stbi_uc ) s;
z->value[c] = (stbi__uint16) i;
if (s <= STBI__ZFAST_BITS) {
int j = stbi__bit_reverse(next_code[s],s);
while (j < (1 << STBI__ZFAST_BITS)) {
z->fast[j] = fastv;
j += (1 << s);
}
}
++next_code[s];
}
}
return 1;
}
// zlib-from-memory implementation for PNG reading
// because PNG allows splitting the zlib stream arbitrarily,
// and it's annoying structurally to have PNG call ZLIB call PNG,
// we require PNG read all the IDATs and combine them into a single
// memory buffer
typedef struct
{
stbi_uc *zbuffer, *zbuffer_end;
int num_bits;
stbi__uint32 code_buffer;
char *zout;
char *zout_start;
char *zout_end;
int z_expandable;
stbi__zhuffman z_length, z_distance;
} stbi__zbuf;
stbi_inline static int stbi__zeof(stbi__zbuf *z)
{
return (z->zbuffer >= z->zbuffer_end);
}
stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z)
{
return stbi__zeof(z) ? 0 : *z->zbuffer++;
}
static void stbi__fill_bits(stbi__zbuf *z)
{
do {
if (z->code_buffer >= (1U << z->num_bits)) {
z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */
return;
}
z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits;
z->num_bits += 8;
} while (z->num_bits <= 24);
}
stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n)
{
unsigned int k;
if (z->num_bits < n) stbi__fill_bits(z);
k = z->code_buffer & ((1 << n) - 1);
z->code_buffer >>= n;
z->num_bits -= n;
return k;
}
static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z)
{
int b,s,k;
// not resolved by fast table, so compute it the slow way
// use jpeg approach, which requires MSbits at top
k = stbi__bit_reverse(a->code_buffer, 16);
for (s=STBI__ZFAST_BITS+1; ; ++s)
if (k < z->maxcode[s])
break;
if (s >= 16) return -1; // invalid code!
// code size is s, so:
b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s];
if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere!
if (z->size[b] != s) return -1; // was originally an assert, but report failure instead.
a->code_buffer >>= s;
a->num_bits -= s;
return z->value[b];
}
stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z)
{
int b,s;
if (a->num_bits < 16) {
if (stbi__zeof(a)) {
return -1; /* report error for unexpected end of data. */
}
stbi__fill_bits(a);
}
b = z->fast[a->code_buffer & STBI__ZFAST_MASK];
if (b) {
s = b >> 9;
a->code_buffer >>= s;
a->num_bits -= s;
return b & 511;
}
return stbi__zhuffman_decode_slowpath(a, z);
}
static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes
{
char *q;
unsigned int cur, limit, old_limit;
z->zout = zout;
if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG");
cur = (unsigned int) (z->zout - z->zout_start);
limit = old_limit = (unsigned) (z->zout_end - z->zout_start);
if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory");
while (cur + n > limit) {
if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory");
limit *= 2;
}
q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit);
STBI_NOTUSED(old_limit);
if (q == NULL) return stbi__err("outofmem", "Out of memory");
z->zout_start = q;
z->zout = q + cur;
z->zout_end = q + limit;
return 1;
}
static const int stbi__zlength_base[31] = {
3,4,5,6,7,8,9,10,11,13,
15,17,19,23,27,31,35,43,51,59,
67,83,99,115,131,163,195,227,258,0,0 };
static const int stbi__zlength_extra[31]=
{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 };
static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0};
static const int stbi__zdist_extra[32] =
{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
static int stbi__parse_huffman_block(stbi__zbuf *a)
{
char *zout = a->zout;
for(;;) {
int z = stbi__zhuffman_decode(a, &a->z_length);
if (z < 256) {
if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes
if (zout >= a->zout_end) {
if (!stbi__zexpand(a, zout, 1)) return 0;
zout = a->zout;
}
*zout++ = (char) z;
} else {
stbi_uc *p;
int len,dist;
if (z == 256) {
a->zout = zout;
return 1;
}
z -= 257;
len = stbi__zlength_base[z];
if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]);
z = stbi__zhuffman_decode(a, &a->z_distance);
if (z < 0) return stbi__err("bad huffman code","Corrupt PNG");
dist = stbi__zdist_base[z];
if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]);
if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG");
if (zout + len > a->zout_end) {
if (!stbi__zexpand(a, zout, len)) return 0;
zout = a->zout;
}
p = (stbi_uc *) (zout - dist);
if (dist == 1) { // run of one byte; common in images.
stbi_uc v = *p;
if (len) { do *zout++ = v; while (--len); }
} else {
if (len) { do *zout++ = *p++; while (--len); }
}
}
}
}
static int stbi__compute_huffman_codes(stbi__zbuf *a)
{
static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 };
stbi__zhuffman z_codelength;
stbi_uc lencodes[286+32+137];//padding for maximum single op
stbi_uc codelength_sizes[19];
int i,n;
int hlit = stbi__zreceive(a,5) + 257;
int hdist = stbi__zreceive(a,5) + 1;
int hclen = stbi__zreceive(a,4) + 4;
int ntot = hlit + hdist;
memset(codelength_sizes, 0, sizeof(codelength_sizes));
for (i=0; i < hclen; ++i) {
int s = stbi__zreceive(a,3);
codelength_sizes[length_dezigzag[i]] = (stbi_uc) s;
}
if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0;
n = 0;
while (n < ntot) {
int c = stbi__zhuffman_decode(a, &z_codelength);
if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG");
if (c < 16)
lencodes[n++] = (stbi_uc) c;
else {
stbi_uc fill = 0;
if (c == 16) {
c = stbi__zreceive(a,2)+3;
if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG");
fill = lencodes[n-1];
} else if (c == 17) {
c = stbi__zreceive(a,3)+3;
} else if (c == 18) {
c = stbi__zreceive(a,7)+11;
} else {
return stbi__err("bad codelengths", "Corrupt PNG");
}
if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG");
memset(lencodes+n, fill, c);
n += c;
}
}
if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG");
if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0;
if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0;
return 1;
}
static int stbi__parse_uncompressed_block(stbi__zbuf *a)
{
stbi_uc header[4];
int len,nlen,k;
if (a->num_bits & 7)
stbi__zreceive(a, a->num_bits & 7); // discard
// drain the bit-packed data into header
k = 0;
while (a->num_bits > 0) {
header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check
a->code_buffer >>= 8;
a->num_bits -= 8;
}
if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG");
// now fill header the normal way
while (k < 4)
header[k++] = stbi__zget8(a);
len = header[1] * 256 + header[0];
nlen = header[3] * 256 + header[2];
if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG");
if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG");
if (a->zout + len > a->zout_end)
if (!stbi__zexpand(a, a->zout, len)) return 0;
memcpy(a->zout, a->zbuffer, len);
a->zbuffer += len;
a->zout += len;
return 1;
}
static int stbi__parse_zlib_header(stbi__zbuf *a)
{
int cmf = stbi__zget8(a);
int cm = cmf & 15;
/* int cinfo = cmf >> 4; */
int flg = stbi__zget8(a);
if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec
if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec
if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png
if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png
// window = 1 << (8 + cinfo)... but who cares, we fully buffer output
return 1;
}
static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] =
{
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8
};
static const stbi_uc stbi__zdefault_distance[32] =
{
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5
};
/*
Init algorithm:
{
int i; // use <= to match clearly with spec
for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8;
for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9;
for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7;
for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8;
for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5;
}
*/
static int stbi__parse_zlib(stbi__zbuf *a, int parse_header)
{
int final, type;
if (parse_header)
if (!stbi__parse_zlib_header(a)) return 0;
a->num_bits = 0;
a->code_buffer = 0;
do {
final = stbi__zreceive(a,1);
type = stbi__zreceive(a,2);
if (type == 0) {
if (!stbi__parse_uncompressed_block(a)) return 0;
} else if (type == 3) {
return 0;
} else {
if (type == 1) {
// use fixed code lengths
if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0;
if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0;
} else {
if (!stbi__compute_huffman_codes(a)) return 0;
}
if (!stbi__parse_huffman_block(a)) return 0;
}
} while (!final);
return 1;
}
static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header)
{
a->zout_start = obuf;
a->zout = obuf;
a->zout_end = obuf + olen;
a->z_expandable = exp;
return stbi__parse_zlib(a, parse_header);
}
STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen)
{
stbi__zbuf a;
char *p = (char *) stbi__malloc(initial_size);
if (p == NULL) return NULL;
a.zbuffer = (stbi_uc *) buffer;
a.zbuffer_end = (stbi_uc *) buffer + len;
if (stbi__do_zlib(&a, p, initial_size, 1, 1)) {
if (outlen) *outlen = (int) (a.zout - a.zout_start);
return a.zout_start;
} else {
STBI_FREE(a.zout_start);
return NULL;
}
}
STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)
{
return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);
}
STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header)
{
stbi__zbuf a;
char *p = (char *) stbi__malloc(initial_size);
if (p == NULL) return NULL;
a.zbuffer = (stbi_uc *) buffer;
a.zbuffer_end = (stbi_uc *) buffer + len;
if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) {
if (outlen) *outlen = (int) (a.zout - a.zout_start);
return a.zout_start;
} else {
STBI_FREE(a.zout_start);
return NULL;
}
}
STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)
{
stbi__zbuf a;
a.zbuffer = (stbi_uc *) ibuffer;
a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
if (stbi__do_zlib(&a, obuffer, olen, 0, 1))
return (int) (a.zout - a.zout_start);
else
return -1;
}
STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen)
{
stbi__zbuf a;
char *p = (char *) stbi__malloc(16384);
if (p == NULL) return NULL;
a.zbuffer = (stbi_uc *) buffer;
a.zbuffer_end = (stbi_uc *) buffer+len;
if (stbi__do_zlib(&a, p, 16384, 1, 0)) {
if (outlen) *outlen = (int) (a.zout - a.zout_start);
return a.zout_start;
} else {
STBI_FREE(a.zout_start);
return NULL;
}
}
STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen)
{
stbi__zbuf a;
a.zbuffer = (stbi_uc *) ibuffer;
a.zbuffer_end = (stbi_uc *) ibuffer + ilen;
if (stbi__do_zlib(&a, obuffer, olen, 0, 0))
return (int) (a.zout - a.zout_start);
else
return -1;
}
// 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
typedef struct
{
stbi__uint32 length;
stbi__uint32 type;
} stbi__pngchunk;
static stbi__pngchunk stbi__get_chunk_header(stbi__context *s)
{
stbi__pngchunk c;
c.length = stbi__get32be(s);
c.type = stbi__get32be(s);
return c;
}
static int stbi__check_png_header(stbi__context *s)
{
static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 };
int i;
for (i=0; i < 8; ++i)
if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG");
return 1;
}
typedef struct
{
stbi__context *s;
stbi_uc *idata, *expanded, *out;
int depth;
} stbi__png;
enum {
STBI__F_none=0,
STBI__F_sub=1,
STBI__F_up=2,
STBI__F_avg=3,
STBI__F_paeth=4,
// synthetic filters used for first scanline to avoid needing a dummy row of 0s
STBI__F_avg_first,
STBI__F_paeth_first
};
static stbi_uc first_row_filter[5] =
{
STBI__F_none,
STBI__F_sub,
STBI__F_none,
STBI__F_avg_first,
STBI__F_paeth_first
};
static int stbi__paeth(int a, int b, int c)
{
int p = a + b - c;
int pa = abs(p-a);
int pb = abs(p-b);
int pc = abs(p-c);
if (pa <= pb && pa <= pc) return a;
if (pb <= pc) return b;
return c;
}
static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 };
// create the png data from post-deflated data
static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color)
{
int bytes = (depth == 16? 2 : 1);
stbi__context *s = a->s;
stbi__uint32 i,j,stride = x*out_n*bytes;
stbi__uint32 img_len, img_width_bytes;
int k;
int img_n = s->img_n; // copy it into a local for later
int output_bytes = out_n*bytes;
int filter_bytes = img_n*bytes;
int width = x;
STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);
a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into
if (!a->out) return stbi__err("outofmem", "Out of memory");
if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG");
img_width_bytes = (((img_n * x * depth) + 7) >> 3);
img_len = (img_width_bytes + 1) * y;
// we used to check for exact match between raw_len and img_len on non-interlaced PNGs,
// but issue #276 reported a PNG in the wild that had extra data at the end (all zeros),
// so just check for raw_len < img_len always.
if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG");
for (j=0; j < y; ++j) {
stbi_uc *cur = a->out + stride*j;
stbi_uc *prior;
int filter = *raw++;
if (filter > 4)
return stbi__err("invalid filter","Corrupt PNG");
if (depth < 8) {
if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG");
cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place
filter_bytes = 1;
width = img_width_bytes;
}
prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above
// if first row, use special filter that doesn't sample previous row
if (j == 0) filter = first_row_filter[filter];
// handle first byte explicitly
for (k=0; k < filter_bytes; ++k) {
switch (filter) {
case STBI__F_none : cur[k] = raw[k]; break;
case STBI__F_sub : cur[k] = raw[k]; break;
case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break;
case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break;
case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break;
case STBI__F_avg_first : cur[k] = raw[k]; break;
case STBI__F_paeth_first: cur[k] = raw[k]; break;
}
}
if (depth == 8) {
if (img_n != out_n)
cur[img_n] = 255; // first pixel
raw += img_n;
cur += out_n;
prior += out_n;
} else if (depth == 16) {
if (img_n != out_n) {
cur[filter_bytes] = 255; // first pixel top byte
cur[filter_bytes+1] = 255; // first pixel bottom byte
}
raw += filter_bytes;
cur += output_bytes;
prior += output_bytes;
} else {
raw += 1;
cur += 1;
prior += 1;
}
// this is a little gross, so that we don't switch per-pixel or per-component
if (depth < 8 || img_n == out_n) {
int nk = (width - 1)*filter_bytes;
#define STBI__CASE(f) \
case f: \
for (k=0; k < nk; ++k)
switch (filter) {
// "none" filter turns into a memcpy here; make that explicit.
case STBI__F_none: memcpy(cur, raw, nk); break;
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break;
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break;
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break;
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break;
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break;
}
#undef STBI__CASE
raw += nk;
} else {
STBI_ASSERT(img_n+1 == out_n);
#define STBI__CASE(f) \
case f: \
for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \
for (k=0; k < filter_bytes; ++k)
switch (filter) {
STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break;
STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break;
STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break;
STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break;
STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break;
STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break;
STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break;
}
#undef STBI__CASE
// the loop above sets the high byte of the pixels' alpha, but for
// 16 bit png files we also need the low byte set. we'll do that here.
if (depth == 16) {
cur = a->out + stride*j; // start at the beginning of the row again
for (i=0; i < x; ++i,cur+=output_bytes) {
cur[filter_bytes+1] = 255;
}
}
}
}
// we make a separate pass to expand bits to pixels; for performance,
// this could run two scanlines behind the above code, so it won't
// intefere with filtering but will still be in the cache.
if (depth < 8) {
for (j=0; j < y; ++j) {
stbi_uc *cur = a->out + stride*j;
stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes;
// unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit
// png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop
stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range
// note that the final byte might overshoot and write more data than desired.
// we can allocate enough data that this never writes out of memory, but it
// could also overwrite the next scanline. can it overwrite non-empty data
// on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel.
// so we need to explicitly clamp the final ones
if (depth == 4) {
for (k=x*img_n; k >= 2; k-=2, ++in) {
*cur++ = scale * ((*in >> 4) );
*cur++ = scale * ((*in ) & 0x0f);
}
if (k > 0) *cur++ = scale * ((*in >> 4) );
} else if (depth == 2) {
for (k=x*img_n; k >= 4; k-=4, ++in) {
*cur++ = scale * ((*in >> 6) );
*cur++ = scale * ((*in >> 4) & 0x03);
*cur++ = scale * ((*in >> 2) & 0x03);
*cur++ = scale * ((*in ) & 0x03);
}
if (k > 0) *cur++ = scale * ((*in >> 6) );
if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03);
if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03);
} else if (depth == 1) {
for (k=x*img_n; k >= 8; k-=8, ++in) {
*cur++ = scale * ((*in >> 7) );
*cur++ = scale * ((*in >> 6) & 0x01);
*cur++ = scale * ((*in >> 5) & 0x01);
*cur++ = scale * ((*in >> 4) & 0x01);
*cur++ = scale * ((*in >> 3) & 0x01);
*cur++ = scale * ((*in >> 2) & 0x01);
*cur++ = scale * ((*in >> 1) & 0x01);
*cur++ = scale * ((*in ) & 0x01);
}
if (k > 0) *cur++ = scale * ((*in >> 7) );
if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01);
if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01);
if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01);
if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01);
if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01);
if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01);
}
if (img_n != out_n) {
int q;
// insert alpha = 255
cur = a->out + stride*j;
if (img_n == 1) {
for (q=x-1; q >= 0; --q) {
cur[q*2+1] = 255;
cur[q*2+0] = cur[q];
}
} else {
STBI_ASSERT(img_n == 3);
for (q=x-1; q >= 0; --q) {
cur[q*4+3] = 255;
cur[q*4+2] = cur[q*3+2];
cur[q*4+1] = cur[q*3+1];
cur[q*4+0] = cur[q*3+0];
}
}
}
}
} else if (depth == 16) {
// force the image data from big-endian to platform-native.
// this is done in a separate pass due to the decoding relying
// on the data being untouched, but could probably be done
// per-line during decode if care is taken.
stbi_uc *cur = a->out;
stbi__uint16 *cur16 = (stbi__uint16*)cur;
for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) {
*cur16 = (cur[0] << 8) | cur[1];
}
}
return 1;
}
static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced)
{
int bytes = (depth == 16 ? 2 : 1);
int out_bytes = out_n * bytes;
stbi_uc *final;
int p;
if (!interlaced)
return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color);
// de-interlacing
final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0);
if (!final) return stbi__err("outofmem", "Out of memory");
for (p=0; p < 7; ++p) {
int xorig[] = { 0,4,0,2,0,1,0 };
int yorig[] = { 0,0,4,0,2,0,1 };
int xspc[] = { 8,8,4,4,2,2,1 };
int yspc[] = { 8,8,8,4,4,2,2 };
int i,j,x,y;
// pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1
x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p];
y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p];
if (x && y) {
stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y;
if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) {
STBI_FREE(final);
return 0;
}
for (j=0; j < y; ++j) {
for (i=0; i < x; ++i) {
int out_y = j*yspc[p]+yorig[p];
int out_x = i*xspc[p]+xorig[p];
memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes,
a->out + (j*x+i)*out_bytes, out_bytes);
}
}
STBI_FREE(a->out);
image_data += img_len;
image_data_len -= img_len;
}
}
a->out = final;
return 1;
}
static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n)
{
stbi__context *s = z->s;
stbi__uint32 i, pixel_count = s->img_x * s->img_y;
stbi_uc *p = z->out;
// compute color-based transparency, assuming we've
// already got 255 as the alpha value in the output
STBI_ASSERT(out_n == 2 || out_n == 4);
if (out_n == 2) {
for (i=0; i < pixel_count; ++i) {
p[1] = (p[0] == tc[0] ? 0 : 255);
p += 2;
}
} else {
for (i=0; i < pixel_count; ++i) {
if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
p[3] = 0;
p += 4;
}
}
return 1;
}
static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n)
{
stbi__context *s = z->s;
stbi__uint32 i, pixel_count = s->img_x * s->img_y;
stbi__uint16 *p = (stbi__uint16*) z->out;
// compute color-based transparency, assuming we've
// already got 65535 as the alpha value in the output
STBI_ASSERT(out_n == 2 || out_n == 4);
if (out_n == 2) {
for (i = 0; i < pixel_count; ++i) {
p[1] = (p[0] == tc[0] ? 0 : 65535);
p += 2;
}
} else {
for (i = 0; i < pixel_count; ++i) {
if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2])
p[3] = 0;
p += 4;
}
}
return 1;
}
static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n)
{
stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y;
stbi_uc *p, *temp_out, *orig = a->out;
p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0);
if (p == NULL) return stbi__err("outofmem", "Out of memory");
// between here and free(out) below, exitting would leak
temp_out = p;
if (pal_img_n == 3) {
for (i=0; i < pixel_count; ++i) {
int n = orig[i]*4;
p[0] = palette[n ];
p[1] = palette[n+1];
p[2] = palette[n+2];
p += 3;
}
} else {
for (i=0; i < pixel_count; ++i) {
int n = orig[i]*4;
p[0] = palette[n ];
p[1] = palette[n+1];
p[2] = palette[n+2];
p[3] = palette[n+3];
p += 4;
}
}
STBI_FREE(a->out);
a->out = temp_out;
STBI_NOTUSED(len);
return 1;
}
static int stbi__unpremultiply_on_load_global = 0;
static int stbi__de_iphone_flag_global = 0;
STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)
{
stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply;
}
STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)
{
stbi__de_iphone_flag_global = flag_true_if_should_convert;
}
#ifndef STBI_THREAD_LOCAL
#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global
#define stbi__de_iphone_flag stbi__de_iphone_flag_global
#else
static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set;
static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set;
STBIDEF void stbi__unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply)
{
stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply;
stbi__unpremultiply_on_load_set = 1;
}
STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert)
{
stbi__de_iphone_flag_local = flag_true_if_should_convert;
stbi__de_iphone_flag_set = 1;
}
#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \
? stbi__unpremultiply_on_load_local \
: stbi__unpremultiply_on_load_global)
#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \
? stbi__de_iphone_flag_local \
: stbi__de_iphone_flag_global)
#endif // STBI_THREAD_LOCAL
static void stbi__de_iphone(stbi__png *z)
{
stbi__context *s = z->s;
stbi__uint32 i, pixel_count = s->img_x * s->img_y;
stbi_uc *p = z->out;
if (s->img_out_n == 3) { // convert bgr to rgb
for (i=0; i < pixel_count; ++i) {
stbi_uc t = p[0];
p[0] = p[2];
p[2] = t;
p += 3;
}
} else {
STBI_ASSERT(s->img_out_n == 4);
if (stbi__unpremultiply_on_load) {
// convert bgr to rgb and unpremultiply
for (i=0; i < pixel_count; ++i) {
stbi_uc a = p[3];
stbi_uc t = p[0];
if (a) {
stbi_uc half = a / 2;
p[0] = (p[2] * 255 + half) / a;
p[1] = (p[1] * 255 + half) / a;
p[2] = ( t * 255 + half) / a;
} else {
p[0] = p[2];
p[2] = t;
}
p += 4;
}
} else {
// convert bgr to rgb
for (i=0; i < pixel_count; ++i) {
stbi_uc t = p[0];
p[0] = p[2];
p[2] = t;
p += 4;
}
}
}
}
#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d))
static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp)
{
stbi_uc palette[1024], pal_img_n=0;
stbi_uc has_trans=0, tc[3]={0};
stbi__uint16 tc16[3];
stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;
int first=1,k,interlace=0, color=0, is_iphone=0;
stbi__context *s = z->s;
z->expanded = NULL;
z->idata = NULL;
z->out = NULL;
if (!stbi__check_png_header(s)) return 0;
if (scan == STBI__SCAN_type) return 1;
for (;;) {
stbi__pngchunk c = stbi__get_chunk_header(s);
switch (c.type) {
case STBI__PNG_TYPE('C','g','B','I'):
is_iphone = 1;
stbi__skip(s, c.length);
break;
case STBI__PNG_TYPE('I','H','D','R'): {
int comp,filter;
if (!first) return stbi__err("multiple IHDR","Corrupt PNG");
first = 0;
if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG");
s->img_x = stbi__get32be(s);
s->img_y = stbi__get32be(s);
if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)");
if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)");
z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only");
color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG");
if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG");
if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG");
comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG");
filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG");
interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG");
if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG");
if (!pal_img_n) {
s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0);
if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode");
if (scan == STBI__SCAN_header) return 1;
} else {
// if paletted, then pal_n is our final components, and
// img_n is # components to decompress/filter.
s->img_n = 1;
if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG");
// if SCAN_header, have to scan to see if we have a tRNS
}
break;
}
case STBI__PNG_TYPE('P','L','T','E'): {
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG");
pal_len = c.length / 3;
if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG");
for (i=0; i < pal_len; ++i) {
palette[i*4+0] = stbi__get8(s);
palette[i*4+1] = stbi__get8(s);
palette[i*4+2] = stbi__get8(s);
palette[i*4+3] = 255;
}
break;
}
case STBI__PNG_TYPE('t','R','N','S'): {
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG");
if (pal_img_n) {
if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; }
if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG");
if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG");
pal_img_n = 4;
for (i=0; i < c.length; ++i)
palette[i*4+3] = stbi__get8(s);
} else {
if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG");
if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG");
has_trans = 1;
if (z->depth == 16) {
for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is
} else {
for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger
}
}
break;
}
case STBI__PNG_TYPE('I','D','A','T'): {
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG");
if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; }
if ((int)(ioff + c.length) < (int)ioff) return 0;
if (ioff + c.length > idata_limit) {
stbi__uint32 idata_limit_old = idata_limit;
stbi_uc *p;
if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096;
while (ioff + c.length > idata_limit)
idata_limit *= 2;
STBI_NOTUSED(idata_limit_old);
p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory");
z->idata = p;
}
if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG");
ioff += c.length;
break;
}
case STBI__PNG_TYPE('I','E','N','D'): {
stbi__uint32 raw_len, bpl;
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if (scan != STBI__SCAN_load) return 1;
if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG");
// initial guess for decoded data size to avoid unnecessary reallocs
bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component
raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */;
z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone);
if (z->expanded == NULL) return 0; // zlib should set error
STBI_FREE(z->idata); z->idata = NULL;
if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans)
s->img_out_n = s->img_n+1;
else
s->img_out_n = s->img_n;
if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0;
if (has_trans) {
if (z->depth == 16) {
if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0;
} else {
if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0;
}
}
if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2)
stbi__de_iphone(z);
if (pal_img_n) {
// pal_img_n == 3 or 4
s->img_n = pal_img_n; // record the actual colors we had
s->img_out_n = pal_img_n;
if (req_comp >= 3) s->img_out_n = req_comp;
if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n))
return 0;
} else if (has_trans) {
// non-paletted image with tRNS -> source image has (constant) alpha
++s->img_n;
}
STBI_FREE(z->expanded); z->expanded = NULL;
// end of PNG chunk, read and skip CRC
stbi__get32be(s);
return 1;
}
default:
// if critical, fail
if (first) return stbi__err("first not IHDR", "Corrupt PNG");
if ((c.type & (1 << 29)) == 0) {
#ifndef STBI_NO_FAILURE_STRINGS
// not threadsafe
static char invalid_chunk[] = "XXXX PNG chunk not known";
invalid_chunk[0] = STBI__BYTECAST(c.type >> 24);
invalid_chunk[1] = STBI__BYTECAST(c.type >> 16);
invalid_chunk[2] = STBI__BYTECAST(c.type >> 8);
invalid_chunk[3] = STBI__BYTECAST(c.type >> 0);
#endif
return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type");
}
stbi__skip(s, c.length);
break;
}
// end of PNG chunk, read and skip CRC
stbi__get32be(s);
}
}
static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri)
{
void *result=NULL;
if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error");
if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) {
if (p->depth <= 8)
ri->bits_per_channel = 8;
else if (p->depth == 16)
ri->bits_per_channel = 16;
else
return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth");
result = p->out;
p->out = NULL;
if (req_comp && req_comp != p->s->img_out_n) {
if (ri->bits_per_channel == 8)
result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
else
result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y);
p->s->img_out_n = req_comp;
if (result == NULL) return result;
}
*x = p->s->img_x;
*y = p->s->img_y;
if (n) *n = p->s->img_n;
}
STBI_FREE(p->out); p->out = NULL;
STBI_FREE(p->expanded); p->expanded = NULL;
STBI_FREE(p->idata); p->idata = NULL;
return result;
}
static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri)
{
stbi__png p;
p.s = s;
return stbi__do_png(&p, x,y,comp,req_comp, ri);
}
static int stbi__png_test(stbi__context *s)
{
int r;
r = stbi__check_png_header(s);
stbi__rewind(s);
return r;
}
static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp)
{
if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) {
stbi__rewind( p->s );
return 0;
}
if (x) *x = p->s->img_x;
if (y) *y = p->s->img_y;
if (comp) *comp = p->s->img_n;
return 1;
}
static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp)
{
stbi__png p;
p.s = s;
return stbi__png_info_raw(&p, x, y, comp);
}
static int stbi__png_is16(stbi__context *s)
{
stbi__png p;
p.s = s;
if (!stbi__png_info_raw(&p, NULL, NULL, NULL))
return 0;
if (p.depth != 16) {
stbi__rewind(p.s);
return 0;
}
return 1;
}
static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp)
{
if (stbi__png_info(s, x, y, comp)) return 1;
// test tga last because it's a crappy test!
return stbi__err("unknown image type", "Image not of any known type, or corrupt");
}
static int stbi__is_16_main(stbi__context *s)
{
if (stbi__png_is16(s)) return 1;
return 0;
}
#ifndef STBI_NO_STDIO
STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp)
{
FILE *f = stbi__fopen(filename, "rb");
int result;
if (!f) return stbi__err("can't fopen", "Unable to open file");
result = stbi_info_from_file(f, x, y, comp);
fclose(f);
return result;
}
STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp)
{
int r;
stbi__context s;
long pos = ftell(f);
stbi__start_file(&s, f);
r = stbi__info_main(&s,x,y,comp);
fseek(f,pos,SEEK_SET);
return r;
}
STBIDEF int stbi_is_16_bit(char const *filename)
{
FILE *f = stbi__fopen(filename, "rb");
int result;
if (!f) return stbi__err("can't fopen", "Unable to open file");
result = stbi_is_16_bit_from_file(f);
fclose(f);
return result;
}
STBIDEF int stbi_is_16_bit_from_file(FILE *f)
{
int r;
stbi__context s;
long pos = ftell(f);
stbi__start_file(&s, f);
r = stbi__is_16_main(&s);
fseek(f,pos,SEEK_SET);
return r;
}
#endif // !STBI_NO_STDIO
STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp)
{
stbi__context s;
stbi__start_mem(&s,buffer,len);
return stbi__info_main(&s,x,y,comp);
}
STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp)
{
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);
return stbi__info_main(&s,x,y,comp);
}
STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len)
{
stbi__context s;
stbi__start_mem(&s,buffer,len);
return stbi__is_16_main(&s);
}
STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user)
{
stbi__context s;
stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user);
return stbi__is_16_main(&s);
}
#endif // STB_IMAGE_IMPLEMENTATION
/*
------------------------------------------------------------------------------
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.
------------------------------------------------------------------------------
*/