nanovg 0.5.0.0 → 0.5.1.0
raw patch · 8 files changed
+14072/−32 lines, 8 filesdep ~basePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
Files
- cbits/nanovg_wrapper.h +44/−0
- nanovg.cabal +9/−3
- nanovg/src/fontstash.h +1725/−0
- nanovg/src/nanovg.c +158/−29
- nanovg/src/nanovg.h +681/−0
- nanovg/src/nanovg_gl.h +1592/−0
- nanovg/src/stb_image.h +6614/−0
- nanovg/src/stb_truetype.h +3249/−0
+ cbits/nanovg_wrapper.h view
@@ -0,0 +1,44 @@+#ifndef NANOVG_WRAPPER_H+#define NANOVG_WRAPPER_H++#include "nanovg.h"++void nvgRGB_(unsigned char r, unsigned char g, unsigned char b, NVGcolor* out);++void nvgRGBf_(float r, float g, float b, NVGcolor* out);++void nvgRGBA_(unsigned char r, unsigned char g, unsigned char b, unsigned char a, NVGcolor* out);++void nvgRGBAf_(float r, float g, float b, float a, NVGcolor* out);++void nvgLerpRGBA_(NVGcolor* c0, NVGcolor* c1, float u, NVGcolor* out);++void nvgTransRGBA_(NVGcolor* c0, unsigned char a, NVGcolor* out);++void nvgTransRGBAf_(NVGcolor* c0, float a, NVGcolor* out);++void nvgHSL_(float h, float s, float l, NVGcolor* out);++void nvgHSLA_(float h, float s, float l, unsigned char a, NVGcolor* out);++void nvgLinearGradient_(NVGcontext* ctx, float sx, float sy, float ex, float ey,+ NVGcolor* icol, NVGcolor* ocol, NVGpaint* out);++void nvgBoxGradient_(NVGcontext* ctx, float x, float y, float w, float h,+ float r, float f, NVGcolor* icol, NVGcolor* ocol, NVGpaint* out);++void nvgRadialGradient_(NVGcontext* ctx, float cx, float cy, float inr, float outr,+ NVGcolor* icol, NVGcolor* ocol, NVGpaint* out);++void nvgImagePattern_(NVGcontext* ctx, float ox, float oy, float ex, float ey,+ float angle, int image, float alpha, NVGpaint* out);++void nvgStrokePaint_(NVGcontext *ctx, NVGpaint *paint);++void nvgStrokeColor_(NVGcontext *ctx, NVGcolor *color);++void nvgFillPaint_(NVGcontext *ctx, NVGpaint *paint);++void nvgFillColor_(NVGcontext *ctx, NVGcolor *color);++#endif // NANOVG_WRAPPER_H
nanovg.cabal view
@@ -1,5 +1,5 @@ name: nanovg-version: 0.5.0.0+version: 0.5.1.0 synopsis: Haskell bindings for nanovg description: Raw bindings to the OpenGL vector graphics library NanoVG homepage: https://github.com/cocreature/nanovg-hs@@ -14,6 +14,8 @@ README.md CHANGELOG.md cabal-version: >=1.10+tested-with: GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.1+ source-repository head type: git location: https://github.com/cocreature/nanovg-hs@@ -40,7 +42,7 @@ NanoVG.Internal.Text NanoVG.Internal.Transformation NanoVG.Internal.Types- build-depends: base >=4.8 && <5.0+ build-depends: base >= 4.7 && <5.0 , bytestring >= 0.10 && < 0.11 , containers >= 0.5 && < 0.6 , text >= 1.2 && < 1.3@@ -49,9 +51,12 @@ default-language: Haskell2010 include-dirs: nanovg/src cbits- includes: nanovg.h+ install-includes: fontstash.h+ nanovg.h nanovg_gl.h nanovg_wrapper.h+ stb_image.h+ stb_truetype.h c-sources: nanovg/src/nanovg.c cbits/nanovg_wrapper.c cbits/nanovg_gl.c@@ -59,6 +64,7 @@ ghc-options: -Wall extra-libraries: GLU, GL, m, GLEW ghc-options: -pgml gcc "-optl-Wl,--whole-archive" "-optl-Wl,-lGLEW" "-optl-Wl,--no-whole-archive"+ build-tools: c2hs executable example00 hs-source-dirs: example
+ nanovg/src/fontstash.h view
@@ -0,0 +1,1725 @@+//+// Copyright (c) 2009-2013 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.+//++#ifndef FONS_H+#define FONS_H++#define FONS_INVALID -1++enum FONSflags {+ FONS_ZERO_TOPLEFT = 1,+ FONS_ZERO_BOTTOMLEFT = 2,+};++enum FONSalign {+ // Horizontal align+ FONS_ALIGN_LEFT = 1<<0, // Default+ FONS_ALIGN_CENTER = 1<<1,+ FONS_ALIGN_RIGHT = 1<<2,+ // Vertical align+ FONS_ALIGN_TOP = 1<<3,+ FONS_ALIGN_MIDDLE = 1<<4,+ FONS_ALIGN_BOTTOM = 1<<5,+ FONS_ALIGN_BASELINE = 1<<6, // Default+};++enum FONSerrorCode {+ // Font atlas is full.+ FONS_ATLAS_FULL = 1,+ // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE.+ FONS_SCRATCH_FULL = 2,+ // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES.+ FONS_STATES_OVERFLOW = 3,+ // Trying to pop too many states fonsPopState().+ FONS_STATES_UNDERFLOW = 4,+};++struct FONSparams {+ int width, height;+ unsigned char flags;+ void* userPtr;+ int (*renderCreate)(void* uptr, int width, int height);+ int (*renderResize)(void* uptr, int width, int height);+ void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data);+ void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts);+ void (*renderDelete)(void* uptr);+};+typedef struct FONSparams FONSparams;++struct FONSquad+{+ float x0,y0,s0,t0;+ float x1,y1,s1,t1;+};+typedef struct FONSquad FONSquad;++struct FONStextIter {+ float x, y, nextx, nexty, scale, spacing;+ unsigned int codepoint;+ short isize, iblur;+ struct FONSfont* font;+ int prevGlyphIndex;+ const char* str;+ const char* next;+ const char* end;+ unsigned int utf8state;+};+typedef struct FONStextIter FONStextIter;++typedef struct FONScontext FONScontext;++// Constructor and destructor.+FONScontext* fonsCreateInternal(FONSparams* params);+void fonsDeleteInternal(FONScontext* s);++void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr);+// Returns current atlas size.+void fonsGetAtlasSize(FONScontext* s, int* width, int* height);+// Expands the atlas size.+int fonsExpandAtlas(FONScontext* s, int width, int height);+// Resets the whole stash.+int fonsResetAtlas(FONScontext* stash, int width, int height);++// Add fonts+int fonsAddFont(FONScontext* s, const char* name, const char* path);+int fonsAddFontMem(FONScontext* s, const char* name, unsigned char* data, int ndata, int freeData);+int fonsGetFontByName(FONScontext* s, const char* name);++// State handling+void fonsPushState(FONScontext* s);+void fonsPopState(FONScontext* s);+void fonsClearState(FONScontext* s);++// State setting+void fonsSetSize(FONScontext* s, float size);+void fonsSetColor(FONScontext* s, unsigned int color);+void fonsSetSpacing(FONScontext* s, float spacing);+void fonsSetBlur(FONScontext* s, float blur);+void fonsSetAlign(FONScontext* s, int align);+void fonsSetFont(FONScontext* s, int font);++// Draw text+float fonsDrawText(FONScontext* s, float x, float y, const char* string, const char* end);++// Measure text+float fonsTextBounds(FONScontext* s, float x, float y, const char* string, const char* end, float* bounds);+void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy);+void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh);++// Text iterator+int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end);+int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad);++// Pull texture changes+const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height);+int fonsValidateTexture(FONScontext* s, int* dirty);++// Draws the stash texture for debugging+void fonsDrawDebug(FONScontext* s, float x, float y);++#endif // FONTSTASH_H+++#ifdef FONTSTASH_IMPLEMENTATION++#define FONS_NOTUSED(v) (void)sizeof(v)++#ifdef FONS_USE_FREETYPE++#include <ft2build.h>+#include FT_FREETYPE_H+#include FT_ADVANCES_H+#include <math.h>++struct FONSttFontImpl {+ FT_Face font;+};+typedef struct FONSttFontImpl FONSttFontImpl;++static FT_Library ftLibrary;++int fons__tt_init(FONScontext *context)+{+ FT_Error ftError;+ FONS_NOTUSED(context);+ ftError = FT_Init_FreeType(&ftLibrary);+ return ftError == 0;+}++int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize)+{+ FT_Error ftError;+ FONS_NOTUSED(context);++ //font->font.userdata = stash;+ ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font);+ return ftError == 0;+}++void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap)+{+ *ascent = font->font->ascender;+ *descent = font->font->descender;+ *lineGap = font->font->height - (*ascent - *descent);+}++float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size)+{+ return size / (font->font->ascender - font->font->descender);+}++int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint)+{+ return FT_Get_Char_Index(font->font, codepoint);+}++int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale,+ int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1)+{+ FT_Error ftError;+ FT_GlyphSlot ftGlyph;+ FT_Fixed advFixed;+ FONS_NOTUSED(scale);++ ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender)));+ if (ftError) return 0;+ ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER);+ if (ftError) return 0;+ ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed);+ if (ftError) return 0;+ ftGlyph = font->font->glyph;+ *advance = (int)advFixed;+ *lsb = (int)ftGlyph->metrics.horiBearingX;+ *x0 = ftGlyph->bitmap_left;+ *x1 = *x0 + ftGlyph->bitmap.width;+ *y0 = -ftGlyph->bitmap_top;+ *y1 = *y0 + ftGlyph->bitmap.rows;+ return 1;+}++void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride,+ float scaleX, float scaleY, int glyph)+{+ FT_GlyphSlot ftGlyph = font->font->glyph;+ int ftGlyphOffset = 0;+ int x, y;+ FONS_NOTUSED(outWidth);+ FONS_NOTUSED(outHeight);+ FONS_NOTUSED(scaleX);+ FONS_NOTUSED(scaleY);+ FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap++ for ( y = 0; y < ftGlyph->bitmap.rows; y++ ) {+ for ( x = 0; x < ftGlyph->bitmap.width; x++ ) {+ output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++];+ }+ }+}++int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)+{+ FT_Vector ftKerning;+ FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning);+ return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer+}++#else++#define STB_TRUETYPE_IMPLEMENTATION+static void* fons__tmpalloc(size_t size, void* up);+static void fons__tmpfree(void* ptr, void* up);+#define STBTT_malloc(x,u) fons__tmpalloc(x,u)+#define STBTT_free(x,u) fons__tmpfree(x,u)+#include "stb_truetype.h"++struct FONSttFontImpl {+ stbtt_fontinfo font;+};+typedef struct FONSttFontImpl FONSttFontImpl;++int fons__tt_init(FONScontext *context)+{+ FONS_NOTUSED(context);+ return 1;+}++int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize)+{+ int stbError;+ FONS_NOTUSED(dataSize);++ font->font.userdata = context;+ stbError = stbtt_InitFont(&font->font, data, 0);+ return stbError;+}++void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap)+{+ stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap);+}++float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size)+{+ return stbtt_ScaleForPixelHeight(&font->font, size);+}++int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint)+{+ return stbtt_FindGlyphIndex(&font->font, codepoint);+}++int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale,+ int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1)+{+ FONS_NOTUSED(size);+ stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb);+ stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1);+ return 1;+}++void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride,+ float scaleX, float scaleY, int glyph)+{+ stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph);+}++int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2)+{+ return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2);+}++#endif++#ifndef FONS_SCRATCH_BUF_SIZE+# define FONS_SCRATCH_BUF_SIZE 64000+#endif+#ifndef FONS_HASH_LUT_SIZE+# define FONS_HASH_LUT_SIZE 256+#endif+#ifndef FONS_INIT_FONTS+# define FONS_INIT_FONTS 4+#endif+#ifndef FONS_INIT_GLYPHS+# define FONS_INIT_GLYPHS 256+#endif+#ifndef FONS_INIT_ATLAS_NODES+# define FONS_INIT_ATLAS_NODES 256+#endif+#ifndef FONS_VERTEX_COUNT+# define FONS_VERTEX_COUNT 1024+#endif+#ifndef FONS_MAX_STATES+# define FONS_MAX_STATES 20+#endif+#ifndef FONS_MAX_FALLBACKS+# define FONS_MAX_FALLBACKS 20+#endif++static unsigned int fons__hashint(unsigned int a)+{+ a += ~(a<<15);+ a ^= (a>>10);+ a += (a<<3);+ a ^= (a>>6);+ a += ~(a<<11);+ a ^= (a>>16);+ return a;+}++static int fons__mini(int a, int b)+{+ return a < b ? a : b;+}++static int fons__maxi(int a, int b)+{+ return a > b ? a : b;+}++struct FONSglyph+{+ unsigned int codepoint;+ int index;+ int next;+ short size, blur;+ short x0,y0,x1,y1;+ short xadv,xoff,yoff;+};+typedef struct FONSglyph FONSglyph;++struct FONSfont+{+ FONSttFontImpl font;+ char name[64];+ unsigned char* data;+ int dataSize;+ unsigned char freeData;+ float ascender;+ float descender;+ float lineh;+ FONSglyph* glyphs;+ int cglyphs;+ int nglyphs;+ int lut[FONS_HASH_LUT_SIZE];+ int fallbacks[FONS_MAX_FALLBACKS];+ int nfallbacks;+};+typedef struct FONSfont FONSfont;++struct FONSstate+{+ int font;+ int align;+ float size;+ unsigned int color;+ float blur;+ float spacing;+};+typedef struct FONSstate FONSstate;++struct FONSatlasNode {+ short x, y, width;+};+typedef struct FONSatlasNode FONSatlasNode;++struct FONSatlas+{+ int width, height;+ FONSatlasNode* nodes;+ int nnodes;+ int cnodes;+};+typedef struct FONSatlas FONSatlas;++struct FONScontext+{+ FONSparams params;+ float itw,ith;+ unsigned char* texData;+ int dirtyRect[4];+ FONSfont** fonts;+ FONSatlas* atlas;+ int cfonts;+ int nfonts;+ float verts[FONS_VERTEX_COUNT*2];+ float tcoords[FONS_VERTEX_COUNT*2];+ unsigned int colors[FONS_VERTEX_COUNT];+ int nverts;+ unsigned char* scratch;+ int nscratch;+ FONSstate states[FONS_MAX_STATES];+ int nstates;+ void (*handleError)(void* uptr, int error, int val);+ void* errorUptr;+};++#ifdef STB_TRUETYPE_IMPLEMENTATION++static void* fons__tmpalloc(size_t size, void* up)+{+ unsigned char* ptr;+ FONScontext* stash = (FONScontext*)up;++ // 16-byte align the returned pointer+ size = (size + 0xf) & ~0xf;++ if (stash->nscratch+(int)size > FONS_SCRATCH_BUF_SIZE) {+ if (stash->handleError)+ stash->handleError(stash->errorUptr, FONS_SCRATCH_FULL, stash->nscratch+(int)size);+ return NULL;+ }+ ptr = stash->scratch + stash->nscratch;+ stash->nscratch += (int)size;+ return ptr;+}++static void fons__tmpfree(void* ptr, void* up)+{+ (void)ptr;+ (void)up;+ // empty+}++#endif // STB_TRUETYPE_IMPLEMENTATION++// Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>+// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.++#define FONS_UTF8_ACCEPT 0+#define FONS_UTF8_REJECT 12++static unsigned int fons__decutf8(unsigned int* state, unsigned int* codep, unsigned int byte)+{+ static const unsigned char utf8d[] = {+ // The first part of the table maps bytes to character classes that+ // to reduce the size of the transition table and create bitmasks.+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 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,7,7,7,7,7,7,7,7,+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,+ 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,++ // The second part is a transition table that maps a combination+ // of a state of the automaton and a character class to a state.+ 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,+ 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,+ 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,+ 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,+ 12,36,12,12,12,12,12,12,12,12,12,12,+ };++ unsigned int type = utf8d[byte];++ *codep = (*state != FONS_UTF8_ACCEPT) ?+ (byte & 0x3fu) | (*codep << 6) :+ (0xff >> type) & (byte);++ *state = utf8d[256 + *state + type];+ return *state;+}++// Atlas based on Skyline Bin Packer by Jukka Jylänki++static void fons__deleteAtlas(FONSatlas* atlas)+{+ if (atlas == NULL) return;+ if (atlas->nodes != NULL) free(atlas->nodes);+ free(atlas);+}++static FONSatlas* fons__allocAtlas(int w, int h, int nnodes)+{+ FONSatlas* atlas = NULL;++ // Allocate memory for the font stash.+ atlas = (FONSatlas*)malloc(sizeof(FONSatlas));+ if (atlas == NULL) goto error;+ memset(atlas, 0, sizeof(FONSatlas));++ atlas->width = w;+ atlas->height = h;++ // Allocate space for skyline nodes+ atlas->nodes = (FONSatlasNode*)malloc(sizeof(FONSatlasNode) * nnodes);+ if (atlas->nodes == NULL) goto error;+ memset(atlas->nodes, 0, sizeof(FONSatlasNode) * nnodes);+ atlas->nnodes = 0;+ atlas->cnodes = nnodes;++ // Init root node.+ atlas->nodes[0].x = 0;+ atlas->nodes[0].y = 0;+ atlas->nodes[0].width = (short)w;+ atlas->nnodes++;++ return atlas;++error:+ if (atlas) fons__deleteAtlas(atlas);+ return NULL;+}++static int fons__atlasInsertNode(FONSatlas* atlas, int idx, int x, int y, int w)+{+ int i;+ // Insert node+ if (atlas->nnodes+1 > atlas->cnodes) {+ atlas->cnodes = atlas->cnodes == 0 ? 8 : atlas->cnodes * 2;+ atlas->nodes = (FONSatlasNode*)realloc(atlas->nodes, sizeof(FONSatlasNode) * atlas->cnodes);+ if (atlas->nodes == NULL)+ return 0;+ }+ for (i = atlas->nnodes; i > idx; i--)+ atlas->nodes[i] = atlas->nodes[i-1];+ atlas->nodes[idx].x = (short)x;+ atlas->nodes[idx].y = (short)y;+ atlas->nodes[idx].width = (short)w;+ atlas->nnodes++;++ return 1;+}++static void fons__atlasRemoveNode(FONSatlas* atlas, int idx)+{+ int i;+ if (atlas->nnodes == 0) return;+ for (i = idx; i < atlas->nnodes-1; i++)+ atlas->nodes[i] = atlas->nodes[i+1];+ atlas->nnodes--;+}++static void fons__atlasExpand(FONSatlas* atlas, int w, int h)+{+ // Insert node for empty space+ if (w > atlas->width)+ fons__atlasInsertNode(atlas, atlas->nnodes, atlas->width, 0, w - atlas->width);+ atlas->width = w;+ atlas->height = h;+}++static void fons__atlasReset(FONSatlas* atlas, int w, int h)+{+ atlas->width = w;+ atlas->height = h;+ atlas->nnodes = 0;++ // Init root node.+ atlas->nodes[0].x = 0;+ atlas->nodes[0].y = 0;+ atlas->nodes[0].width = (short)w;+ atlas->nnodes++;+}++static int fons__atlasAddSkylineLevel(FONSatlas* atlas, int idx, int x, int y, int w, int h)+{+ int i;++ // Insert new node+ if (fons__atlasInsertNode(atlas, idx, x, y+h, w) == 0)+ return 0;++ // Delete skyline segments that fall under the shadow of the new segment.+ for (i = idx+1; i < atlas->nnodes; i++) {+ if (atlas->nodes[i].x < atlas->nodes[i-1].x + atlas->nodes[i-1].width) {+ int shrink = atlas->nodes[i-1].x + atlas->nodes[i-1].width - atlas->nodes[i].x;+ atlas->nodes[i].x += (short)shrink;+ atlas->nodes[i].width -= (short)shrink;+ if (atlas->nodes[i].width <= 0) {+ fons__atlasRemoveNode(atlas, i);+ i--;+ } else {+ break;+ }+ } else {+ break;+ }+ }++ // Merge same height skyline segments that are next to each other.+ for (i = 0; i < atlas->nnodes-1; i++) {+ if (atlas->nodes[i].y == atlas->nodes[i+1].y) {+ atlas->nodes[i].width += atlas->nodes[i+1].width;+ fons__atlasRemoveNode(atlas, i+1);+ i--;+ }+ }++ return 1;+}++static int fons__atlasRectFits(FONSatlas* atlas, int i, int w, int h)+{+ // Checks if there is enough space at the location of skyline span 'i',+ // and return the max height of all skyline spans under that at that location,+ // (think tetris block being dropped at that position). Or -1 if no space found.+ int x = atlas->nodes[i].x;+ int y = atlas->nodes[i].y;+ int spaceLeft;+ if (x + w > atlas->width)+ return -1;+ spaceLeft = w;+ while (spaceLeft > 0) {+ if (i == atlas->nnodes) return -1;+ y = fons__maxi(y, atlas->nodes[i].y);+ if (y + h > atlas->height) return -1;+ spaceLeft -= atlas->nodes[i].width;+ ++i;+ }+ return y;+}++static int fons__atlasAddRect(FONSatlas* atlas, int rw, int rh, int* rx, int* ry)+{+ int besth = atlas->height, bestw = atlas->width, besti = -1;+ int bestx = -1, besty = -1, i;++ // Bottom left fit heuristic.+ for (i = 0; i < atlas->nnodes; i++) {+ int y = fons__atlasRectFits(atlas, i, rw, rh);+ if (y != -1) {+ if (y + rh < besth || (y + rh == besth && atlas->nodes[i].width < bestw)) {+ besti = i;+ bestw = atlas->nodes[i].width;+ besth = y + rh;+ bestx = atlas->nodes[i].x;+ besty = y;+ }+ }+ }++ if (besti == -1)+ return 0;++ // Perform the actual packing.+ if (fons__atlasAddSkylineLevel(atlas, besti, bestx, besty, rw, rh) == 0)+ return 0;++ *rx = bestx;+ *ry = besty;++ return 1;+}++static void fons__addWhiteRect(FONScontext* stash, int w, int h)+{+ int x, y, gx, gy;+ unsigned char* dst;+ if (fons__atlasAddRect(stash->atlas, w, h, &gx, &gy) == 0)+ return;++ // Rasterize+ dst = &stash->texData[gx + gy * stash->params.width];+ for (y = 0; y < h; y++) {+ for (x = 0; x < w; x++)+ dst[x] = 0xff;+ dst += stash->params.width;+ }++ stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], gx);+ stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], gy);+ stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], gx+w);+ stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], gy+h);+}++FONScontext* fonsCreateInternal(FONSparams* params)+{+ FONScontext* stash = NULL;++ // Allocate memory for the font stash.+ stash = (FONScontext*)malloc(sizeof(FONScontext));+ if (stash == NULL) goto error;+ memset(stash, 0, sizeof(FONScontext));++ stash->params = *params;++ // Allocate scratch buffer.+ stash->scratch = (unsigned char*)malloc(FONS_SCRATCH_BUF_SIZE);+ if (stash->scratch == NULL) goto error;++ // Initialize implementation library+ if (!fons__tt_init(stash)) goto error;++ if (stash->params.renderCreate != NULL) {+ if (stash->params.renderCreate(stash->params.userPtr, stash->params.width, stash->params.height) == 0)+ goto error;+ }++ stash->atlas = fons__allocAtlas(stash->params.width, stash->params.height, FONS_INIT_ATLAS_NODES);+ if (stash->atlas == NULL) goto error;++ // Allocate space for fonts.+ stash->fonts = (FONSfont**)malloc(sizeof(FONSfont*) * FONS_INIT_FONTS);+ if (stash->fonts == NULL) goto error;+ memset(stash->fonts, 0, sizeof(FONSfont*) * FONS_INIT_FONTS);+ stash->cfonts = FONS_INIT_FONTS;+ stash->nfonts = 0;++ // Create texture for the cache.+ stash->itw = 1.0f/stash->params.width;+ stash->ith = 1.0f/stash->params.height;+ stash->texData = (unsigned char*)malloc(stash->params.width * stash->params.height);+ if (stash->texData == NULL) goto error;+ memset(stash->texData, 0, stash->params.width * stash->params.height);++ stash->dirtyRect[0] = stash->params.width;+ stash->dirtyRect[1] = stash->params.height;+ stash->dirtyRect[2] = 0;+ stash->dirtyRect[3] = 0;++ // Add white rect at 0,0 for debug drawing.+ fons__addWhiteRect(stash, 2,2);++ fonsPushState(stash);+ fonsClearState(stash);++ return stash;++error:+ fonsDeleteInternal(stash);+ return NULL;+}++static FONSstate* fons__getState(FONScontext* stash)+{+ return &stash->states[stash->nstates-1];+}++int fonsAddFallbackFont(FONScontext* stash, int base, int fallback)+{+ FONSfont* baseFont = stash->fonts[base];+ if (baseFont->nfallbacks < FONS_MAX_FALLBACKS) {+ baseFont->fallbacks[baseFont->nfallbacks++] = fallback;+ return 1;+ }+ return 0;+}++void fonsSetSize(FONScontext* stash, float size)+{+ fons__getState(stash)->size = size;+}++void fonsSetColor(FONScontext* stash, unsigned int color)+{+ fons__getState(stash)->color = color;+}++void fonsSetSpacing(FONScontext* stash, float spacing)+{+ fons__getState(stash)->spacing = spacing;+}++void fonsSetBlur(FONScontext* stash, float blur)+{+ fons__getState(stash)->blur = blur;+}++void fonsSetAlign(FONScontext* stash, int align)+{+ fons__getState(stash)->align = align;+}++void fonsSetFont(FONScontext* stash, int font)+{+ fons__getState(stash)->font = font;+}++void fonsPushState(FONScontext* stash)+{+ if (stash->nstates >= FONS_MAX_STATES) {+ if (stash->handleError)+ stash->handleError(stash->errorUptr, FONS_STATES_OVERFLOW, 0);+ return;+ }+ if (stash->nstates > 0)+ memcpy(&stash->states[stash->nstates], &stash->states[stash->nstates-1], sizeof(FONSstate));+ stash->nstates++;+}++void fonsPopState(FONScontext* stash)+{+ if (stash->nstates <= 1) {+ if (stash->handleError)+ stash->handleError(stash->errorUptr, FONS_STATES_UNDERFLOW, 0);+ return;+ }+ stash->nstates--;+}++void fonsClearState(FONScontext* stash)+{+ FONSstate* state = fons__getState(stash);+ state->size = 12.0f;+ state->color = 0xffffffff;+ state->font = 0;+ state->blur = 0;+ state->spacing = 0;+ state->align = FONS_ALIGN_LEFT | FONS_ALIGN_BASELINE;+}++static void fons__freeFont(FONSfont* font)+{+ if (font == NULL) return;+ if (font->glyphs) free(font->glyphs);+ if (font->freeData && font->data) free(font->data);+ free(font);+}++static int fons__allocFont(FONScontext* stash)+{+ FONSfont* font = NULL;+ if (stash->nfonts+1 > stash->cfonts) {+ stash->cfonts = stash->cfonts == 0 ? 8 : stash->cfonts * 2;+ stash->fonts = (FONSfont**)realloc(stash->fonts, sizeof(FONSfont*) * stash->cfonts);+ if (stash->fonts == NULL)+ return -1;+ }+ font = (FONSfont*)malloc(sizeof(FONSfont));+ if (font == NULL) goto error;+ memset(font, 0, sizeof(FONSfont));++ font->glyphs = (FONSglyph*)malloc(sizeof(FONSglyph) * FONS_INIT_GLYPHS);+ if (font->glyphs == NULL) goto error;+ font->cglyphs = FONS_INIT_GLYPHS;+ font->nglyphs = 0;++ stash->fonts[stash->nfonts++] = font;+ return stash->nfonts-1;++error:+ fons__freeFont(font);++ return FONS_INVALID;+}++int fonsAddFont(FONScontext* stash, const char* name, const char* path)+{+ FILE* fp = 0;+ int dataSize = 0, readed;+ unsigned char* data = NULL;++ // Read in the font data.+ fp = fopen(path, "rb");+ if (fp == NULL) goto error;+ fseek(fp,0,SEEK_END);+ dataSize = (int)ftell(fp);+ fseek(fp,0,SEEK_SET);+ data = (unsigned char*)malloc(dataSize);+ if (data == NULL) goto error;+ readed = fread(data, 1, dataSize, fp);+ fclose(fp);+ fp = 0;+ if (readed != dataSize) goto error;++ return fonsAddFontMem(stash, name, data, dataSize, 1);++error:+ if (data) free(data);+ if (fp) fclose(fp);+ return FONS_INVALID;+}++int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData)+{+ int i, ascent, descent, fh, lineGap;+ FONSfont* font;++ int idx = fons__allocFont(stash);+ if (idx == FONS_INVALID)+ return FONS_INVALID;++ font = stash->fonts[idx];++ strncpy(font->name, name, sizeof(font->name));+ font->name[sizeof(font->name)-1] = '\0';++ // Init hash lookup.+ for (i = 0; i < FONS_HASH_LUT_SIZE; ++i)+ font->lut[i] = -1;++ // Read in the font data.+ font->dataSize = dataSize;+ font->data = data;+ font->freeData = (unsigned char)freeData;++ // Init font+ stash->nscratch = 0;+ if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error;++ // Store normalized line height. The real line height is got+ // by multiplying the lineh by font size.+ fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap);+ fh = ascent - descent;+ font->ascender = (float)ascent / (float)fh;+ font->descender = (float)descent / (float)fh;+ font->lineh = (float)(fh + lineGap) / (float)fh;++ return idx;++error:+ fons__freeFont(font);+ stash->nfonts--;+ return FONS_INVALID;+}++int fonsGetFontByName(FONScontext* s, const char* name)+{+ int i;+ for (i = 0; i < s->nfonts; i++) {+ if (strcmp(s->fonts[i]->name, name) == 0)+ return i;+ }+ return FONS_INVALID;+}+++static FONSglyph* fons__allocGlyph(FONSfont* font)+{+ if (font->nglyphs+1 > font->cglyphs) {+ font->cglyphs = font->cglyphs == 0 ? 8 : font->cglyphs * 2;+ font->glyphs = (FONSglyph*)realloc(font->glyphs, sizeof(FONSglyph) * font->cglyphs);+ if (font->glyphs == NULL) return NULL;+ }+ font->nglyphs++;+ return &font->glyphs[font->nglyphs-1];+}+++// Based on Exponential blur, Jani Huhtanen, 2006++#define APREC 16+#define ZPREC 7++static void fons__blurCols(unsigned char* dst, int w, int h, int dstStride, int alpha)+{+ int x, y;+ for (y = 0; y < h; y++) {+ int z = 0; // force zero border+ for (x = 1; x < w; x++) {+ z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC;+ dst[x] = (unsigned char)(z >> ZPREC);+ }+ dst[w-1] = 0; // force zero border+ z = 0;+ for (x = w-2; x >= 0; x--) {+ z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC;+ dst[x] = (unsigned char)(z >> ZPREC);+ }+ dst[0] = 0; // force zero border+ dst += dstStride;+ }+}++static void fons__blurRows(unsigned char* dst, int w, int h, int dstStride, int alpha)+{+ int x, y;+ for (x = 0; x < w; x++) {+ int z = 0; // force zero border+ for (y = dstStride; y < h*dstStride; y += dstStride) {+ z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC;+ dst[y] = (unsigned char)(z >> ZPREC);+ }+ dst[(h-1)*dstStride] = 0; // force zero border+ z = 0;+ for (y = (h-2)*dstStride; y >= 0; y -= dstStride) {+ z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC;+ dst[y] = (unsigned char)(z >> ZPREC);+ }+ dst[0] = 0; // force zero border+ dst++;+ }+}+++static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int dstStride, int blur)+{+ int alpha;+ float sigma;+ (void)stash;++ if (blur < 1)+ return;+ // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity)+ sigma = (float)blur * 0.57735f; // 1 / sqrt(3)+ alpha = (int)((1<<APREC) * (1.0f - expf(-2.3f / (sigma+1.0f))));+ fons__blurRows(dst, w, h, dstStride, alpha);+ fons__blurCols(dst, w, h, dstStride, alpha);+ fons__blurRows(dst, w, h, dstStride, alpha);+ fons__blurCols(dst, w, h, dstStride, alpha);+// fons__blurrows(dst, w, h, dstStride, alpha);+// fons__blurcols(dst, w, h, dstStride, alpha);+}++static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint,+ short isize, short iblur)+{+ int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y;+ float scale;+ FONSglyph* glyph = NULL;+ unsigned int h;+ float size = isize/10.0f;+ int pad, added;+ unsigned char* bdst;+ unsigned char* dst;+ FONSfont* renderFont = font;++ if (isize < 2) return NULL;+ if (iblur > 20) iblur = 20;+ pad = iblur+2;++ // Reset allocator.+ stash->nscratch = 0;++ // Find code point and size.+ h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1);+ i = font->lut[h];+ while (i != -1) {+ if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur)+ return &font->glyphs[i];+ i = font->glyphs[i].next;+ }++ // Could not find glyph, create it.+ g = fons__tt_getGlyphIndex(&font->font, codepoint);+ // Try to find the glyph in fallback fonts.+ if (g == 0) {+ for (i = 0; i < font->nfallbacks; ++i) {+ FONSfont* fallbackFont = stash->fonts[font->fallbacks[i]];+ int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont->font, codepoint);+ if (fallbackIndex != 0) {+ g = fallbackIndex;+ renderFont = fallbackFont;+ break;+ }+ }+ // It is possible that we did not find a fallback glyph.+ // In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph.+ }+ scale = fons__tt_getPixelHeightScale(&renderFont->font, size);+ fons__tt_buildGlyphBitmap(&renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1);+ gw = x1-x0 + pad*2;+ gh = y1-y0 + pad*2;++ // Find free spot for the rect in the atlas+ added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);+ if (added == 0 && stash->handleError != NULL) {+ // Atlas is full, let the user to resize the atlas (or not), and try again.+ stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0);+ added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);+ }+ if (added == 0) return NULL;++ // Init glyph.+ glyph = fons__allocGlyph(font);+ glyph->codepoint = codepoint;+ glyph->size = isize;+ glyph->blur = iblur;+ glyph->index = g;+ glyph->x0 = (short)gx;+ glyph->y0 = (short)gy;+ glyph->x1 = (short)(glyph->x0+gw);+ glyph->y1 = (short)(glyph->y0+gh);+ glyph->xadv = (short)(scale * advance * 10.0f);+ glyph->xoff = (short)(x0 - pad);+ glyph->yoff = (short)(y0 - pad);+ glyph->next = 0;++ // Insert char to hash lookup.+ glyph->next = font->lut[h];+ font->lut[h] = font->nglyphs-1;++ // Rasterize+ dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width];+ fons__tt_renderGlyphBitmap(&renderFont->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g);++ // Make sure there is one pixel empty border.+ dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];+ for (y = 0; y < gh; y++) {+ dst[y*stash->params.width] = 0;+ dst[gw-1 + y*stash->params.width] = 0;+ }+ for (x = 0; x < gw; x++) {+ dst[x] = 0;+ dst[x + (gh-1)*stash->params.width] = 0;+ }++ // Debug code to color the glyph background+/* unsigned char* fdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];+ for (y = 0; y < gh; y++) {+ for (x = 0; x < gw; x++) {+ int a = (int)fdst[x+y*stash->params.width] + 20;+ if (a > 255) a = 255;+ fdst[x+y*stash->params.width] = a;+ }+ }*/++ // Blur+ if (iblur > 0) {+ stash->nscratch = 0;+ bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];+ fons__blur(stash, bdst, gw,gh, stash->params.width, iblur);+ }++ stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0);+ stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], glyph->y0);+ stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], glyph->x1);+ stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], glyph->y1);++ return glyph;+}++static void fons__getQuad(FONScontext* stash, FONSfont* font,+ int prevGlyphIndex, FONSglyph* glyph,+ float scale, float spacing, float* x, float* y, FONSquad* q)+{+ float rx,ry,xoff,yoff,x0,y0,x1,y1;++ if (prevGlyphIndex != -1) {+ float adv = fons__tt_getGlyphKernAdvance(&font->font, prevGlyphIndex, glyph->index) * scale;+ *x += (int)(adv + spacing + 0.5f);+ }++ // Each glyph has 2px border to allow good interpolation,+ // one pixel to prevent leaking, and one to allow good interpolation for rendering.+ // Inset the texture region by one pixel for correct interpolation.+ xoff = (short)(glyph->xoff+1);+ yoff = (short)(glyph->yoff+1);+ x0 = (float)(glyph->x0+1);+ y0 = (float)(glyph->y0+1);+ x1 = (float)(glyph->x1-1);+ y1 = (float)(glyph->y1-1);++ if (stash->params.flags & FONS_ZERO_TOPLEFT) {+ rx = (float)(int)(*x + xoff);+ ry = (float)(int)(*y + yoff);++ q->x0 = rx;+ q->y0 = ry;+ q->x1 = rx + x1 - x0;+ q->y1 = ry + y1 - y0;++ q->s0 = x0 * stash->itw;+ q->t0 = y0 * stash->ith;+ q->s1 = x1 * stash->itw;+ q->t1 = y1 * stash->ith;+ } else {+ rx = (float)(int)(*x + xoff);+ ry = (float)(int)(*y - yoff);++ q->x0 = rx;+ q->y0 = ry;+ q->x1 = rx + x1 - x0;+ q->y1 = ry - y1 + y0;++ q->s0 = x0 * stash->itw;+ q->t0 = y0 * stash->ith;+ q->s1 = x1 * stash->itw;+ q->t1 = y1 * stash->ith;+ }++ *x += (int)(glyph->xadv / 10.0f + 0.5f);+}++static void fons__flush(FONScontext* stash)+{+ // Flush texture+ if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) {+ if (stash->params.renderUpdate != NULL)+ stash->params.renderUpdate(stash->params.userPtr, stash->dirtyRect, stash->texData);+ // Reset dirty rect+ stash->dirtyRect[0] = stash->params.width;+ stash->dirtyRect[1] = stash->params.height;+ stash->dirtyRect[2] = 0;+ stash->dirtyRect[3] = 0;+ }++ // Flush triangles+ if (stash->nverts > 0) {+ if (stash->params.renderDraw != NULL)+ stash->params.renderDraw(stash->params.userPtr, stash->verts, stash->tcoords, stash->colors, stash->nverts);+ stash->nverts = 0;+ }+}++static __inline void fons__vertex(FONScontext* stash, float x, float y, float s, float t, unsigned int c)+{+ stash->verts[stash->nverts*2+0] = x;+ stash->verts[stash->nverts*2+1] = y;+ stash->tcoords[stash->nverts*2+0] = s;+ stash->tcoords[stash->nverts*2+1] = t;+ stash->colors[stash->nverts] = c;+ stash->nverts++;+}++static float fons__getVertAlign(FONScontext* stash, FONSfont* font, int align, short isize)+{+ if (stash->params.flags & FONS_ZERO_TOPLEFT) {+ if (align & FONS_ALIGN_TOP) {+ return font->ascender * (float)isize/10.0f;+ } else if (align & FONS_ALIGN_MIDDLE) {+ return (font->ascender + font->descender) / 2.0f * (float)isize/10.0f;+ } else if (align & FONS_ALIGN_BASELINE) {+ return 0.0f;+ } else if (align & FONS_ALIGN_BOTTOM) {+ return font->descender * (float)isize/10.0f;+ }+ } else {+ if (align & FONS_ALIGN_TOP) {+ return -font->ascender * (float)isize/10.0f;+ } else if (align & FONS_ALIGN_MIDDLE) {+ return -(font->ascender + font->descender) / 2.0f * (float)isize/10.0f;+ } else if (align & FONS_ALIGN_BASELINE) {+ return 0.0f;+ } else if (align & FONS_ALIGN_BOTTOM) {+ return -font->descender * (float)isize/10.0f;+ }+ }+ return 0.0;+}++float fonsDrawText(FONScontext* stash,+ float x, float y,+ const char* str, const char* end)+{+ FONSstate* state = fons__getState(stash);+ unsigned int codepoint;+ unsigned int utf8state = 0;+ FONSglyph* glyph = NULL;+ FONSquad q;+ int prevGlyphIndex = -1;+ short isize = (short)(state->size*10.0f);+ short iblur = (short)state->blur;+ float scale;+ FONSfont* font;+ float width;++ if (stash == NULL) return x;+ if (state->font < 0 || state->font >= stash->nfonts) return x;+ font = stash->fonts[state->font];+ if (font->data == NULL) return x;++ scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f);++ if (end == NULL)+ end = str + strlen(str);++ // Align horizontally+ if (state->align & FONS_ALIGN_LEFT) {+ // empty+ } else if (state->align & FONS_ALIGN_RIGHT) {+ width = fonsTextBounds(stash, x,y, str, end, NULL);+ x -= width;+ } else if (state->align & FONS_ALIGN_CENTER) {+ width = fonsTextBounds(stash, x,y, str, end, NULL);+ x -= width * 0.5f;+ }+ // Align vertically.+ y += fons__getVertAlign(stash, font, state->align, isize);++ for (; str != end; ++str) {+ if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str))+ continue;+ glyph = fons__getGlyph(stash, font, codepoint, isize, iblur);+ if (glyph != NULL) {+ fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);++ if (stash->nverts+6 > FONS_VERTEX_COUNT)+ fons__flush(stash);++ fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color);+ fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color);+ fons__vertex(stash, q.x1, q.y0, q.s1, q.t0, state->color);++ fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color);+ fons__vertex(stash, q.x0, q.y1, q.s0, q.t1, state->color);+ fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color);+ }+ prevGlyphIndex = glyph != NULL ? glyph->index : -1;+ }+ fons__flush(stash);++ return x;+}++int fonsTextIterInit(FONScontext* stash, FONStextIter* iter,+ float x, float y, const char* str, const char* end)+{+ FONSstate* state = fons__getState(stash);+ float width;++ memset(iter, 0, sizeof(*iter));++ if (stash == NULL) return 0;+ if (state->font < 0 || state->font >= stash->nfonts) return 0;+ iter->font = stash->fonts[state->font];+ if (iter->font->data == NULL) return 0;++ iter->isize = (short)(state->size*10.0f);+ iter->iblur = (short)state->blur;+ iter->scale = fons__tt_getPixelHeightScale(&iter->font->font, (float)iter->isize/10.0f);++ // Align horizontally+ if (state->align & FONS_ALIGN_LEFT) {+ // empty+ } else if (state->align & FONS_ALIGN_RIGHT) {+ width = fonsTextBounds(stash, x,y, str, end, NULL);+ x -= width;+ } else if (state->align & FONS_ALIGN_CENTER) {+ width = fonsTextBounds(stash, x,y, str, end, NULL);+ x -= width * 0.5f;+ }+ // Align vertically.+ y += fons__getVertAlign(stash, iter->font, state->align, iter->isize);++ if (end == NULL)+ end = str + strlen(str);++ iter->x = iter->nextx = x;+ iter->y = iter->nexty = y;+ iter->spacing = state->spacing;+ iter->str = str;+ iter->next = str;+ iter->end = end;+ iter->codepoint = 0;+ iter->prevGlyphIndex = -1;++ return 1;+}++int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad)+{+ FONSglyph* glyph = NULL;+ const char* str = iter->next;+ iter->str = iter->next;++ if (str == iter->end)+ return 0;++ for (; str != iter->end; str++) {+ if (fons__decutf8(&iter->utf8state, &iter->codepoint, *(const unsigned char*)str))+ continue;+ str++;+ // Get glyph and quad+ iter->x = iter->nextx;+ iter->y = iter->nexty;+ glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur);+ if (glyph != NULL)+ fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad);+ iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1;+ break;+ }+ iter->next = str;++ return 1;+}++void fonsDrawDebug(FONScontext* stash, float x, float y)+{+ int i;+ int w = stash->params.width;+ int h = stash->params.height;+ float u = w == 0 ? 0 : (1.0f / w);+ float v = h == 0 ? 0 : (1.0f / h);++ if (stash->nverts+6+6 > FONS_VERTEX_COUNT)+ fons__flush(stash);++ // Draw background+ fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff);+ fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff);+ fons__vertex(stash, x+w, y+0, u, v, 0x0fffffff);++ fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff);+ fons__vertex(stash, x+0, y+h, u, v, 0x0fffffff);+ fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff);++ // Draw texture+ fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff);+ fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff);+ fons__vertex(stash, x+w, y+0, 1, 0, 0xffffffff);++ fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff);+ fons__vertex(stash, x+0, y+h, 0, 1, 0xffffffff);+ fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff);++ // Drawbug draw atlas+ for (i = 0; i < stash->atlas->nnodes; i++) {+ FONSatlasNode* n = &stash->atlas->nodes[i];++ if (stash->nverts+6 > FONS_VERTEX_COUNT)+ fons__flush(stash);++ fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff);+ fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff);+ fons__vertex(stash, x+n->x+n->width, y+n->y+0, u, v, 0xc00000ff);++ fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff);+ fons__vertex(stash, x+n->x+0, y+n->y+1, u, v, 0xc00000ff);+ fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff);+ }++ fons__flush(stash);+}++float fonsTextBounds(FONScontext* stash,+ float x, float y,+ const char* str, const char* end,+ float* bounds)+{+ FONSstate* state = fons__getState(stash);+ unsigned int codepoint;+ unsigned int utf8state = 0;+ FONSquad q;+ FONSglyph* glyph = NULL;+ int prevGlyphIndex = -1;+ short isize = (short)(state->size*10.0f);+ short iblur = (short)state->blur;+ float scale;+ FONSfont* font;+ float startx, advance;+ float minx, miny, maxx, maxy;++ if (stash == NULL) return 0;+ if (state->font < 0 || state->font >= stash->nfonts) return 0;+ font = stash->fonts[state->font];+ if (font->data == NULL) return 0;++ scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f);++ // Align vertically.+ y += fons__getVertAlign(stash, font, state->align, isize);++ minx = maxx = x;+ miny = maxy = y;+ startx = x;++ if (end == NULL)+ end = str + strlen(str);++ for (; str != end; ++str) {+ if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str))+ continue;+ glyph = fons__getGlyph(stash, font, codepoint, isize, iblur);+ if (glyph != NULL) {+ fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);+ if (q.x0 < minx) minx = q.x0;+ if (q.x1 > maxx) maxx = q.x1;+ if (stash->params.flags & FONS_ZERO_TOPLEFT) {+ if (q.y0 < miny) miny = q.y0;+ if (q.y1 > maxy) maxy = q.y1;+ } else {+ if (q.y1 < miny) miny = q.y1;+ if (q.y0 > maxy) maxy = q.y0;+ }+ }+ prevGlyphIndex = glyph != NULL ? glyph->index : -1;+ }++ advance = x - startx;++ // Align horizontally+ if (state->align & FONS_ALIGN_LEFT) {+ // empty+ } else if (state->align & FONS_ALIGN_RIGHT) {+ minx -= advance;+ maxx -= advance;+ } else if (state->align & FONS_ALIGN_CENTER) {+ minx -= advance * 0.5f;+ maxx -= advance * 0.5f;+ }++ if (bounds) {+ bounds[0] = minx;+ bounds[1] = miny;+ bounds[2] = maxx;+ bounds[3] = maxy;+ }++ return advance;+}++void fonsVertMetrics(FONScontext* stash,+ float* ascender, float* descender, float* lineh)+{+ FONSfont* font;+ FONSstate* state = fons__getState(stash);+ short isize;++ if (stash == NULL) return;+ if (state->font < 0 || state->font >= stash->nfonts) return;+ font = stash->fonts[state->font];+ isize = (short)(state->size*10.0f);+ if (font->data == NULL) return;++ if (ascender)+ *ascender = font->ascender*isize/10.0f;+ if (descender)+ *descender = font->descender*isize/10.0f;+ if (lineh)+ *lineh = font->lineh*isize/10.0f;+}++void fonsLineBounds(FONScontext* stash, float y, float* miny, float* maxy)+{+ FONSfont* font;+ FONSstate* state = fons__getState(stash);+ short isize;++ if (stash == NULL) return;+ if (state->font < 0 || state->font >= stash->nfonts) return;+ font = stash->fonts[state->font];+ isize = (short)(state->size*10.0f);+ if (font->data == NULL) return;++ y += fons__getVertAlign(stash, font, state->align, isize);++ if (stash->params.flags & FONS_ZERO_TOPLEFT) {+ *miny = y - font->ascender * (float)isize/10.0f;+ *maxy = *miny + font->lineh*isize/10.0f;+ } else {+ *maxy = y + font->descender * (float)isize/10.0f;+ *miny = *maxy - font->lineh*isize/10.0f;+ }+}++const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height)+{+ if (width != NULL)+ *width = stash->params.width;+ if (height != NULL)+ *height = stash->params.height;+ return stash->texData;+}++int fonsValidateTexture(FONScontext* stash, int* dirty)+{+ if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) {+ dirty[0] = stash->dirtyRect[0];+ dirty[1] = stash->dirtyRect[1];+ dirty[2] = stash->dirtyRect[2];+ dirty[3] = stash->dirtyRect[3];+ // Reset dirty rect+ stash->dirtyRect[0] = stash->params.width;+ stash->dirtyRect[1] = stash->params.height;+ stash->dirtyRect[2] = 0;+ stash->dirtyRect[3] = 0;+ return 1;+ }+ return 0;+}++void fonsDeleteInternal(FONScontext* stash)+{+ int i;+ if (stash == NULL) return;++ if (stash->params.renderDelete)+ stash->params.renderDelete(stash->params.userPtr);++ for (i = 0; i < stash->nfonts; ++i)+ fons__freeFont(stash->fonts[i]);++ if (stash->atlas) fons__deleteAtlas(stash->atlas);+ if (stash->fonts) free(stash->fonts);+ if (stash->texData) free(stash->texData);+ if (stash->scratch) free(stash->scratch);+ free(stash);+}++void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr)+{+ if (stash == NULL) return;+ stash->handleError = callback;+ stash->errorUptr = uptr;+}++void fonsGetAtlasSize(FONScontext* stash, int* width, int* height)+{+ if (stash == NULL) return;+ *width = stash->params.width;+ *height = stash->params.height;+}++int fonsExpandAtlas(FONScontext* stash, int width, int height)+{+ int i, maxy = 0;+ unsigned char* data = NULL;+ if (stash == NULL) return 0;++ width = fons__maxi(width, stash->params.width);+ height = fons__maxi(height, stash->params.height);++ if (width == stash->params.width && height == stash->params.height)+ return 1;++ // Flush pending glyphs.+ fons__flush(stash);++ // Create new texture+ if (stash->params.renderResize != NULL) {+ if (stash->params.renderResize(stash->params.userPtr, width, height) == 0)+ return 0;+ }+ // Copy old texture data over.+ data = (unsigned char*)malloc(width * height);+ if (data == NULL)+ return 0;+ for (i = 0; i < stash->params.height; i++) {+ unsigned char* dst = &data[i*width];+ unsigned char* src = &stash->texData[i*stash->params.width];+ memcpy(dst, src, stash->params.width);+ if (width > stash->params.width)+ memset(dst+stash->params.width, 0, width - stash->params.width);+ }+ if (height > stash->params.height)+ memset(&data[stash->params.height * width], 0, (height - stash->params.height) * width);++ free(stash->texData);+ stash->texData = data;++ // Increase atlas size+ fons__atlasExpand(stash->atlas, width, height);++ // Add existing data as dirty.+ for (i = 0; i < stash->atlas->nnodes; i++)+ maxy = fons__maxi(maxy, stash->atlas->nodes[i].y);+ stash->dirtyRect[0] = 0;+ stash->dirtyRect[1] = 0;+ stash->dirtyRect[2] = stash->params.width;+ stash->dirtyRect[3] = maxy;++ stash->params.width = width;+ stash->params.height = height;+ stash->itw = 1.0f/stash->params.width;+ stash->ith = 1.0f/stash->params.height;++ return 1;+}++int fonsResetAtlas(FONScontext* stash, int width, int height)+{+ int i, j;+ if (stash == NULL) return 0;++ // Flush pending glyphs.+ fons__flush(stash);++ // Create new texture+ if (stash->params.renderResize != NULL) {+ if (stash->params.renderResize(stash->params.userPtr, width, height) == 0)+ return 0;+ }++ // Reset atlas+ fons__atlasReset(stash->atlas, width, height);++ // Clear texture data.+ stash->texData = (unsigned char*)realloc(stash->texData, width * height);+ if (stash->texData == NULL) return 0;+ memset(stash->texData, 0, width * height);++ // Reset dirty rect+ stash->dirtyRect[0] = width;+ stash->dirtyRect[1] = height;+ stash->dirtyRect[2] = 0;+ stash->dirtyRect[3] = 0;++ // Reset cached glyphs+ for (i = 0; i < stash->nfonts; i++) {+ FONSfont* font = stash->fonts[i];+ font->nglyphs = 0;+ for (j = 0; j < FONS_HASH_LUT_SIZE; j++)+ font->lut[j] = -1;+ }++ stash->params.width = width;+ stash->params.height = height;+ stash->itw = 1.0f/stash->params.width;+ stash->ith = 1.0f/stash->params.height;++ // Add white rect at 0,0 for debug drawing.+ fons__addWhiteRect(stash, 2,2);++ return 1;+}+++#endif
nanovg/src/nanovg.c view
@@ -66,6 +66,7 @@ }; struct NVGstate {+ NVGcompositeOperationState compositeOperation; NVGpaint fill; NVGpaint stroke; float strokeWidth;@@ -203,6 +204,84 @@ ctx->devicePxRatio = ratio; } +static NVGcompositeOperationState nvg__compositeOperationState(int op)+{+ int sfactor, dfactor;++ if (op == NVG_SOURCE_OVER)+ {+ sfactor = NVG_ONE;+ dfactor = NVG_ONE_MINUS_SRC_ALPHA;+ }+ else if (op == NVG_SOURCE_IN)+ {+ sfactor = NVG_DST_ALPHA;+ dfactor = NVG_ZERO;+ }+ else if (op == NVG_SOURCE_OUT)+ {+ sfactor = NVG_ONE_MINUS_DST_ALPHA;+ dfactor = NVG_ZERO;+ }+ else if (op == NVG_ATOP)+ {+ sfactor = NVG_DST_ALPHA;+ dfactor = NVG_ONE_MINUS_SRC_ALPHA;+ }+ else if (op == NVG_DESTINATION_OVER)+ {+ sfactor = NVG_ONE_MINUS_DST_ALPHA;+ dfactor = NVG_ONE;+ }+ else if (op == NVG_DESTINATION_IN)+ {+ sfactor = NVG_ZERO;+ dfactor = NVG_SRC_ALPHA;+ }+ else if (op == NVG_DESTINATION_OUT)+ {+ sfactor = NVG_ZERO;+ dfactor = NVG_ONE_MINUS_SRC_ALPHA;+ }+ else if (op == NVG_DESTINATION_ATOP)+ {+ sfactor = NVG_ONE_MINUS_DST_ALPHA;+ dfactor = NVG_SRC_ALPHA;+ }+ else if (op == NVG_LIGHTER)+ {+ sfactor = NVG_ONE;+ dfactor = NVG_ONE;+ }+ else if (op == NVG_COPY)+ {+ sfactor = NVG_ONE;+ dfactor = NVG_ZERO;+ }+ else if (op == NVG_XOR)+ {+ sfactor = NVG_ONE_MINUS_DST_ALPHA;+ dfactor = NVG_ONE_MINUS_SRC_ALPHA;+ }+ else+ {+ sfactor = NVG_ONE;+ dfactor = NVG_ZERO;+ }++ NVGcompositeOperationState state;+ state.srcRGB = sfactor;+ state.dstRGB = dfactor;+ state.srcAlpha = sfactor;+ state.dstAlpha = dfactor;+ return state;+}++static NVGstate* nvg__getState(NVGcontext* ctx)+{+ return &ctx->states[ctx->nstates-1];+}+ NVGcontext* nvgCreateInternal(NVGparams* params) { FONSparams fontParams;@@ -295,7 +374,7 @@ nvg__setDevicePixelRatio(ctx, devicePixelRatio); - ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight);+ ctx->params.renderViewport(ctx->params.userPtr, windowWidth, windowHeight, devicePixelRatio); ctx->drawCallCount = 0; ctx->fillTriCount = 0;@@ -310,7 +389,8 @@ void nvgEndFrame(NVGcontext* ctx) {- ctx->params.renderFlush(ctx->params.userPtr);+ NVGstate* state = nvg__getState(ctx);+ ctx->params.renderFlush(ctx->params.userPtr, state->compositeOperation); if (ctx->fontImageIdx != 0) { int fontImage = ctx->fontImages[ctx->fontImageIdx]; int i, j, iw, ih;@@ -386,7 +466,7 @@ { int i; float oneminu;- NVGcolor cint;+ NVGcolor cint = {{{0}}}; u = nvg__clampf(u, 0.0f, 1.0f); oneminu = 1.0f - u;@@ -433,12 +513,6 @@ return col; } --static NVGstate* nvg__getState(NVGcontext* ctx)-{- return &ctx->states[ctx->nstates-1];-}- void nvgTransformIdentity(float* t) { t[0] = 1.0f; t[1] = 0.0f;@@ -571,6 +645,7 @@ nvg__setPaintColor(&state->fill, nvgRGBA(255,255,255,255)); nvg__setPaintColor(&state->stroke, nvgRGBA(0,0,0,255));+ state->compositeOperation = nvg__compositeOperationState(NVG_SOURCE_OVER); state->strokeWidth = 1.0f; state->miterLimit = 10.0f; state->lineCap = NVG_BUTT;@@ -939,6 +1014,30 @@ state->scissor.extent[1] = -1.0f; } +// Global composite operation.+void nvgGlobalCompositeOperation(NVGcontext* ctx, int op)+{+ NVGstate* state = nvg__getState(ctx);+ state->compositeOperation = nvg__compositeOperationState(op);+}++void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor)+{+ nvgGlobalCompositeBlendFuncSeparate(ctx, sfactor, dfactor, sfactor, dfactor);+}++void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha)+{+ NVGcompositeOperationState op;+ op.srcRGB = srcRGB;+ op.dstRGB = dstRGB;+ op.srcAlpha = srcAlpha;+ op.dstAlpha = dstAlpha;++ NVGstate* state = nvg__getState(ctx);+ state->compositeOperation = op;+}+ static int nvg__ptEquals(float x1, float y1, float x2, float y2, float tol) { float dx = x2 - x1;@@ -2025,22 +2124,31 @@ void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r) {- if (r < 0.1f) {- nvgRect(ctx, x,y,w,h);+ nvgRoundedRectVarying(ctx, x, y, w, h, r, r, r, r);+}++void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft)+{+ if(radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) {+ nvgRect(ctx, x, y, w, h); return;- }- else {- float rx = nvg__minf(r, nvg__absf(w)*0.5f) * nvg__signf(w), ry = nvg__minf(r, nvg__absf(h)*0.5f) * nvg__signf(h);+ } else {+ float halfw = nvg__absf(w)*0.5f;+ float halfh = nvg__absf(h)*0.5f;+ float rxBL = nvg__minf(radBottomLeft, halfw) * nvg__signf(w), ryBL = nvg__minf(radBottomLeft, halfh) * nvg__signf(h);+ float rxBR = nvg__minf(radBottomRight, halfw) * nvg__signf(w), ryBR = nvg__minf(radBottomRight, halfh) * nvg__signf(h);+ float rxTR = nvg__minf(radTopRight, halfw) * nvg__signf(w), ryTR = nvg__minf(radTopRight, halfh) * nvg__signf(h);+ float rxTL = nvg__minf(radTopLeft, halfw) * nvg__signf(w), ryTL = nvg__minf(radTopLeft, halfh) * nvg__signf(h); float vals[] = {- NVG_MOVETO, x, y+ry,- NVG_LINETO, x, y+h-ry,- NVG_BEZIERTO, x, y+h-ry*(1-NVG_KAPPA90), x+rx*(1-NVG_KAPPA90), y+h, x+rx, y+h,- NVG_LINETO, x+w-rx, y+h,- NVG_BEZIERTO, x+w-rx*(1-NVG_KAPPA90), y+h, x+w, y+h-ry*(1-NVG_KAPPA90), x+w, y+h-ry,- NVG_LINETO, x+w, y+ry,- NVG_BEZIERTO, x+w, y+ry*(1-NVG_KAPPA90), x+w-rx*(1-NVG_KAPPA90), y, x+w-rx, y,- NVG_LINETO, x+rx, y,- NVG_BEZIERTO, x+rx*(1-NVG_KAPPA90), y, x, y+ry*(1-NVG_KAPPA90), x, y+ry,+ NVG_MOVETO, x, y + ryTL,+ NVG_LINETO, x, y + h - ryBL,+ NVG_BEZIERTO, x, y + h - ryBL*(1 - NVG_KAPPA90), x + rxBL*(1 - NVG_KAPPA90), y + h, x + rxBL, y + h,+ NVG_LINETO, x + w - rxBR, y + h,+ NVG_BEZIERTO, x + w - rxBR*(1 - NVG_KAPPA90), y + h, x + w, y + h - ryBR*(1 - NVG_KAPPA90), x + w, y + h - ryBR,+ NVG_LINETO, x + w, y + ryTR,+ NVG_BEZIERTO, x + w, y + ryTR*(1 - NVG_KAPPA90), x + w - rxTR*(1 - NVG_KAPPA90), y, x + w - rxTR, y,+ NVG_LINETO, x + rxTL, y,+ NVG_BEZIERTO, x + rxTL*(1 - NVG_KAPPA90), y, x, y + ryTL*(1 - NVG_KAPPA90), x, y + ryTL, NVG_CLOSE }; nvg__appendCommands(ctx, vals, NVG_COUNTOF(vals));@@ -2173,6 +2281,18 @@ return fonsGetFontByName(ctx->fs, name); } ++int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont)+{+ if(baseFont == -1 || fallbackFont == -1) return 0;+ return fonsAddFallbackFont(ctx->fs, baseFont, fallbackFont);+}++int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont)+{+ return nvgAddFallbackFontId(ctx, nvgFindFont(ctx, baseFont), nvgFindFont(ctx, fallbackFont));+}+ // State setting void nvgFontSize(NVGcontext* ctx, float size) {@@ -2434,6 +2554,7 @@ NVG_SPACE, NVG_NEWLINE, NVG_CHAR,+ NVG_CJK_CHAR, }; int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows)@@ -2501,7 +2622,15 @@ type = NVG_NEWLINE; break; default:- type = NVG_CHAR;+ if ((iter.codepoint >= 0x4E00 && iter.codepoint <= 0x9FFF) ||+ (iter.codepoint >= 0x3000 && iter.codepoint <= 0x30FF) ||+ (iter.codepoint >= 0xFF00 && iter.codepoint <= 0xFFEF) ||+ (iter.codepoint >= 0x1100 && iter.codepoint <= 0x11FF) ||+ (iter.codepoint >= 0x3130 && iter.codepoint <= 0x318F) ||+ (iter.codepoint >= 0xAC00 && iter.codepoint <= 0xD7AF))+ type = NVG_CJK_CHAR;+ else+ type = NVG_CHAR; break; } @@ -2528,7 +2657,7 @@ } else { if (rowStart == NULL) { // Skip white space until the beginning of the line- if (type == NVG_CHAR) {+ if (type == NVG_CHAR || type == NVG_CJK_CHAR) { // The current char is the row so far rowStartX = iter.x; rowStart = iter.str;@@ -2548,26 +2677,26 @@ float nextWidth = iter.nextx - rowStartX; // track last non-white space character- if (type == NVG_CHAR) {+ if (type == NVG_CHAR || type == NVG_CJK_CHAR) { rowEnd = iter.next; rowWidth = iter.nextx - rowStartX; rowMaxX = q.x1 - rowStartX; } // track last end of a word- if (ptype == NVG_CHAR && type == NVG_SPACE) {+ if (((ptype == NVG_CHAR || ptype == NVG_CJK_CHAR) && type == NVG_SPACE) || type == NVG_CJK_CHAR) { breakEnd = iter.str; breakWidth = rowWidth; breakMaxX = rowMaxX; } // track last beginning of a word- if (ptype == NVG_SPACE && type == NVG_CHAR) {+ if ((ptype == NVG_SPACE && (type == NVG_CHAR || type == NVG_CJK_CHAR)) || type == NVG_CJK_CHAR) { wordStart = iter.str; wordStartX = iter.x; wordMinX = q.x0 - rowStartX; } // Break to new line when a character is beyond break width.- if (type == NVG_CHAR && nextWidth > breakRowWidth) {+ if ((type == NVG_CHAR || type == NVG_CJK_CHAR) && nextWidth > breakRowWidth) { // The run length is too long, need to break to new line. if (breakEnd == rowStart) { // The current word is longer than the row length, just break it from here.
+ nanovg/src/nanovg.h view
@@ -0,0 +1,681 @@+//+// Copyright (c) 2013 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.+//++#ifndef NANOVG_H+#define NANOVG_H++#ifdef __cplusplus+extern "C" {+#endif++#define NVG_PI 3.14159265358979323846264338327f++#ifdef _MSC_VER+#pragma warning(push)+#pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union+#endif++typedef struct NVGcontext NVGcontext;++struct NVGcolor {+ union {+ float rgba[4];+ struct {+ float r,g,b,a;+ };+ };+};+typedef struct NVGcolor NVGcolor;++struct NVGpaint {+ float xform[6];+ float extent[2];+ float radius;+ float feather;+ NVGcolor innerColor;+ NVGcolor outerColor;+ int image;+};+typedef struct NVGpaint NVGpaint;++enum NVGwinding {+ NVG_CCW = 1, // Winding for solid shapes+ NVG_CW = 2, // Winding for holes+};++enum NVGsolidity {+ NVG_SOLID = 1, // CCW+ NVG_HOLE = 2, // CW+};++enum NVGlineCap {+ NVG_BUTT,+ NVG_ROUND,+ NVG_SQUARE,+ NVG_BEVEL,+ NVG_MITER,+};++enum NVGalign {+ // Horizontal align+ NVG_ALIGN_LEFT = 1<<0, // Default, align text horizontally to left.+ NVG_ALIGN_CENTER = 1<<1, // Align text horizontally to center.+ NVG_ALIGN_RIGHT = 1<<2, // Align text horizontally to right.+ // Vertical align+ NVG_ALIGN_TOP = 1<<3, // Align text vertically to top.+ NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle.+ NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom.+ NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline.+};++enum NVGblendFactor {+ NVG_ZERO = 1<<0,+ NVG_ONE = 1<<1,+ NVG_SRC_COLOR = 1<<2,+ NVG_ONE_MINUS_SRC_COLOR = 1<<3,+ NVG_DST_COLOR = 1<<4,+ NVG_ONE_MINUS_DST_COLOR = 1<<5,+ NVG_SRC_ALPHA = 1<<6,+ NVG_ONE_MINUS_SRC_ALPHA = 1<<7,+ NVG_DST_ALPHA = 1<<8,+ NVG_ONE_MINUS_DST_ALPHA = 1<<9,+ NVG_SRC_ALPHA_SATURATE = 1<<10,+};++enum NVGcompositeOperation {+ NVG_SOURCE_OVER,+ NVG_SOURCE_IN,+ NVG_SOURCE_OUT,+ NVG_ATOP,+ NVG_DESTINATION_OVER,+ NVG_DESTINATION_IN,+ NVG_DESTINATION_OUT,+ NVG_DESTINATION_ATOP,+ NVG_LIGHTER,+ NVG_COPY,+ NVG_XOR,+};++struct NVGcompositeOperationState {+ int srcRGB;+ int dstRGB;+ int srcAlpha;+ int dstAlpha;+};+typedef struct NVGcompositeOperationState NVGcompositeOperationState;++struct NVGglyphPosition {+ const char* str; // Position of the glyph in the input string.+ float x; // The x-coordinate of the logical glyph position.+ float minx, maxx; // The bounds of the glyph shape.+};+typedef struct NVGglyphPosition NVGglyphPosition;++struct NVGtextRow {+ const char* start; // Pointer to the input text where the row starts.+ const char* end; // Pointer to the input text where the row ends (one past the last character).+ const char* next; // Pointer to the beginning of the next row.+ float width; // Logical width of the row.+ float minx, maxx; // Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending.+};+typedef struct NVGtextRow NVGtextRow;++enum NVGimageFlags {+ NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image.+ NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction.+ NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction.+ NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered.+ NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha.+};++// Begin drawing a new frame+// Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame()+// nvgBeginFrame() defines the size of the window to render to in relation currently+// set viewport (i.e. glViewport on GL backends). Device pixel ration allows to+// control the rendering on Hi-DPI devices.+// For example, GLFW returns two dimension for an opened window: window size and+// frame buffer size. In that case you would set windowWidth/Height to the window size+// devicePixelRatio to: frameBufferWidth / windowWidth.+void nvgBeginFrame(NVGcontext* ctx, int windowWidth, int windowHeight, float devicePixelRatio);++// Cancels drawing the current frame.+void nvgCancelFrame(NVGcontext* ctx);++// Ends drawing flushing remaining render state.+void nvgEndFrame(NVGcontext* ctx);++//+// Composite operation+//+// The composite operations in NanoVG are modeled after HTML Canvas API, and+// the blend func is based on OpenGL (see corresponding manuals for more info).+// The colors in the blending state have premultiplied alpha.++// Sets the composite operation. The op parameter should be one of NVGcompositeOperation.+void nvgGlobalCompositeOperation(NVGcontext* ctx, int op);++// Sets the composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor.+void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor);++// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor.+void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha);++//+// Color utils+//+// Colors in NanoVG are stored as unsigned ints in ABGR format.++// Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f).+NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b);++// Returns a color value from red, green, blue values. Alpha will be set to 1.0f.+NVGcolor nvgRGBf(float r, float g, float b);+++// Returns a color value from red, green, blue and alpha values.+NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a);++// Returns a color value from red, green, blue and alpha values.+NVGcolor nvgRGBAf(float r, float g, float b, float a);+++// Linearly interpolates from color c0 to c1, and returns resulting color value.+NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u);++// Sets transparency of a color value.+NVGcolor nvgTransRGBA(NVGcolor c0, unsigned char a);++// Sets transparency of a color value.+NVGcolor nvgTransRGBAf(NVGcolor c0, float a);++// Returns color value specified by hue, saturation and lightness.+// HSL values are all in range [0..1], alpha will be set to 255.+NVGcolor nvgHSL(float h, float s, float l);++// Returns color value specified by hue, saturation and lightness and alpha.+// HSL values are all in range [0..1], alpha in range [0..255]+NVGcolor nvgHSLA(float h, float s, float l, unsigned char a);++//+// State Handling+//+// NanoVG contains state which represents how paths will be rendered.+// The state contains transform, fill and stroke styles, text and font styles,+// and scissor clipping.++// Pushes and saves the current render state into a state stack.+// A matching nvgRestore() must be used to restore the state.+void nvgSave(NVGcontext* ctx);++// Pops and restores current render state.+void nvgRestore(NVGcontext* ctx);++// Resets current render state to default values. Does not affect the render state stack.+void nvgReset(NVGcontext* ctx);++//+// Render styles+//+// Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern.+// Solid color is simply defined as a color value, different kinds of paints can be created+// using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern().+//+// Current render style can be saved and restored using nvgSave() and nvgRestore().++// Sets current stroke style to a solid color.+void nvgStrokeColor(NVGcontext* ctx, NVGcolor color);++// Sets current stroke style to a paint, which can be a one of the gradients or a pattern.+void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint);++// Sets current fill style to a solid color.+void nvgFillColor(NVGcontext* ctx, NVGcolor color);++// Sets current fill style to a paint, which can be a one of the gradients or a pattern.+void nvgFillPaint(NVGcontext* ctx, NVGpaint paint);++// Sets the miter limit of the stroke style.+// Miter limit controls when a sharp corner is beveled.+void nvgMiterLimit(NVGcontext* ctx, float limit);++// Sets the stroke width of the stroke style.+void nvgStrokeWidth(NVGcontext* ctx, float size);++// Sets how the end of the line (cap) is drawn,+// Can be one of: NVG_BUTT (default), NVG_ROUND, NVG_SQUARE.+void nvgLineCap(NVGcontext* ctx, int cap);++// Sets how sharp path corners are drawn.+// Can be one of NVG_MITER (default), NVG_ROUND, NVG_BEVEL.+void nvgLineJoin(NVGcontext* ctx, int join);++// Sets the transparency applied to all rendered shapes.+// Already transparent paths will get proportionally more transparent as well.+void nvgGlobalAlpha(NVGcontext* ctx, float alpha);++//+// Transforms+//+// The paths, gradients, patterns and scissor region are transformed by an transformation+// matrix at the time when they are passed to the API.+// The current transformation matrix is a affine matrix:+// [sx kx tx]+// [ky sy ty]+// [ 0 0 1]+// Where: sx,sy define scaling, kx,ky skewing, and tx,ty translation.+// The last row is assumed to be 0,0,1 and is not stored.+//+// Apart from nvgResetTransform(), each transformation function first creates+// specific transformation matrix and pre-multiplies the current transformation by it.+//+// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore().++// Resets current transform to a identity matrix.+void nvgResetTransform(NVGcontext* ctx);++// Premultiplies current coordinate system by specified matrix.+// The parameters are interpreted as matrix as follows:+// [a c e]+// [b d f]+// [0 0 1]+void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f);++// Translates current coordinate system.+void nvgTranslate(NVGcontext* ctx, float x, float y);++// Rotates current coordinate system. Angle is specified in radians.+void nvgRotate(NVGcontext* ctx, float angle);++// Skews the current coordinate system along X axis. Angle is specified in radians.+void nvgSkewX(NVGcontext* ctx, float angle);++// Skews the current coordinate system along Y axis. Angle is specified in radians.+void nvgSkewY(NVGcontext* ctx, float angle);++// Scales the current coordinate system.+void nvgScale(NVGcontext* ctx, float x, float y);++// Stores the top part (a-f) of the current transformation matrix in to the specified buffer.+// [a c e]+// [b d f]+// [0 0 1]+// There should be space for 6 floats in the return buffer for the values a-f.+void nvgCurrentTransform(NVGcontext* ctx, float* xform);+++// The following functions can be used to make calculations on 2x3 transformation matrices.+// A 2x3 matrix is represented as float[6].++// Sets the transform to identity matrix.+void nvgTransformIdentity(float* dst);++// Sets the transform to translation matrix matrix.+void nvgTransformTranslate(float* dst, float tx, float ty);++// Sets the transform to scale matrix.+void nvgTransformScale(float* dst, float sx, float sy);++// Sets the transform to rotate matrix. Angle is specified in radians.+void nvgTransformRotate(float* dst, float a);++// Sets the transform to skew-x matrix. Angle is specified in radians.+void nvgTransformSkewX(float* dst, float a);++// Sets the transform to skew-y matrix. Angle is specified in radians.+void nvgTransformSkewY(float* dst, float a);++// Sets the transform to the result of multiplication of two transforms, of A = A*B.+void nvgTransformMultiply(float* dst, const float* src);++// Sets the transform to the result of multiplication of two transforms, of A = B*A.+void nvgTransformPremultiply(float* dst, const float* src);++// Sets the destination to inverse of specified transform.+// Returns 1 if the inverse could be calculated, else 0.+int nvgTransformInverse(float* dst, const float* src);++// Transform a point by given transform.+void nvgTransformPoint(float* dstx, float* dsty, const float* xform, float srcx, float srcy);++// Converts degrees to radians and vice versa.+float nvgDegToRad(float deg);+float nvgRadToDeg(float rad);++//+// Images+//+// NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering.+// In addition you can upload your own image. The image loading is provided by stb_image.+// The parameter imageFlags is combination of flags defined in NVGimageFlags.++// Creates image by loading it from the disk from specified file name.+// Returns handle to the image.+int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags);++// Creates image by loading it from the specified chunk of memory.+// Returns handle to the image.+int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata);++// Creates image from specified image data.+// Returns handle to the image.+int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data);++// Updates image data specified by image handle.+void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data);++// Returns the dimensions of a created image.+void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h);++// Deletes created image.+void nvgDeleteImage(NVGcontext* ctx, int image);++//+// Paints+//+// NanoVG supports four types of paints: linear gradient, box gradient, radial gradient and image pattern.+// These can be used as paints for strokes and fills.++// Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates+// of the linear gradient, icol specifies the start color and ocol the end color.+// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().+NVGpaint nvgLinearGradient(NVGcontext* ctx, float sx, float sy, float ex, float ey,+ NVGcolor icol, NVGcolor ocol);++// Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering+// drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle,+// (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry+// the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient.+// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().+NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h,+ float r, float f, NVGcolor icol, NVGcolor ocol);++// Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify+// the inner and outer radius of the gradient, icol specifies the start color and ocol the end color.+// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().+NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr,+ NVGcolor icol, NVGcolor ocol);++// Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern,+// (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render.+// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().+NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey,+ float angle, int image, float alpha);++//+// Scissoring+//+// Scissoring allows you to clip the rendering into a rectangle. This is useful for various+// user interface cases like rendering a text edit or a timeline.++// Sets the current scissor rectangle.+// The scissor rectangle is transformed by the current transform.+void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h);++// Intersects current scissor rectangle with the specified rectangle.+// The scissor rectangle is transformed by the current transform.+// Note: in case the rotation of previous scissor rect differs from+// the current one, the intersection will be done between the specified+// rectangle and the previous scissor rectangle transformed in the current+// transform space. The resulting shape is always rectangle.+void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h);++// Reset and disables scissoring.+void nvgResetScissor(NVGcontext* ctx);++//+// Paths+//+// Drawing a new shape starts with nvgBeginPath(), it clears all the currently defined paths.+// Then you define one or more paths and sub-paths which describe the shape. The are functions+// to draw common shapes like rectangles and circles, and lower level step-by-step functions,+// which allow to define a path curve by curve.+//+// NanoVG uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise+// winding and holes should have counter clockwise order. To specify winding of a path you can+// call nvgPathWinding(). This is useful especially for the common shapes, which are drawn CCW.+//+// Finally you can fill the path using current fill style by calling nvgFill(), and stroke it+// with current stroke style by calling nvgStroke().+//+// The curve segments and sub-paths are transformed by the current transform.++// Clears the current path and sub-paths.+void nvgBeginPath(NVGcontext* ctx);++// Starts new sub-path with specified point as first point.+void nvgMoveTo(NVGcontext* ctx, float x, float y);++// Adds line segment from the last point in the path to the specified point.+void nvgLineTo(NVGcontext* ctx, float x, float y);++// Adds cubic bezier segment from last point in the path via two control points to the specified point.+void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y);++// Adds quadratic bezier segment from last point in the path via a control point to the specified point.+void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y);++// Adds an arc segment at the corner defined by the last path point, and two specified points.+void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius);++// Closes current sub-path with a line segment.+void nvgClosePath(NVGcontext* ctx);++// Sets the current sub-path winding, see NVGwinding and NVGsolidity.+void nvgPathWinding(NVGcontext* ctx, int dir);++// Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r,+// and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW, or NVG_CW).+// Angles are specified in radians.+void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir);++// Creates new rectangle shaped sub-path.+void nvgRect(NVGcontext* ctx, float x, float y, float w, float h);++// Creates new rounded rectangle shaped sub-path.+void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r);++// Creates new rounded rectangle shaped sub-path with varying radii for each corner.+void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft);++// Creates new ellipse shaped sub-path.+void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry);++// Creates new circle shaped sub-path.+void nvgCircle(NVGcontext* ctx, float cx, float cy, float r);++// Fills the current path with current fill style.+void nvgFill(NVGcontext* ctx);++// Fills the current path with current stroke style.+void nvgStroke(NVGcontext* ctx);+++//+// Text+//+// NanoVG allows you to load .ttf files and use the font to render text.+//+// The appearance of the text can be defined by setting the current text style+// and by specifying the fill color. Common text and font settings such as+// font size, letter spacing and text align are supported. Font blur allows you+// to create simple text effects such as drop shadows.+//+// At render time the font face can be set based on the font handles or name.+//+// Font measure functions return values in local space, the calculations are+// carried in the same resolution as the final rendering. This is done because+// the text glyph positions are snapped to the nearest pixels sharp rendering.+//+// The local space means that values are not rotated or scale as per the current+// transformation. For example if you set font size to 12, which would mean that+// line height is 16, then regardless of the current scaling and rotation, the+// returned line height is always 16. Some measures may vary because of the scaling+// since aforementioned pixel snapping.+//+// While this may sound a little odd, the setup allows you to always render the+// same way regardless of scaling. I.e. following works regardless of scaling:+//+// const char* txt = "Text me up.";+// nvgTextBounds(vg, x,y, txt, NULL, bounds);+// nvgBeginPath(vg);+// nvgRoundedRect(vg, bounds[0],bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]);+// nvgFill(vg);+//+// Note: currently only solid color fill is supported for text.++// Creates font by loading it from the disk from specified file name.+// Returns handle to the font.+int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename);++// Creates font by loading it from the specified memory chunk.+// Returns handle to the font.+int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData);++// Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found.+int nvgFindFont(NVGcontext* ctx, const char* name);++// Adds a fallback font by handle.+int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont);++// Adds a fallback font by name.+int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont);++// Sets the font size of current text style.+void nvgFontSize(NVGcontext* ctx, float size);++// Sets the blur of current text style.+void nvgFontBlur(NVGcontext* ctx, float blur);++// Sets the letter spacing of current text style.+void nvgTextLetterSpacing(NVGcontext* ctx, float spacing);++// Sets the proportional line height of current text style. The line height is specified as multiple of font size.+void nvgTextLineHeight(NVGcontext* ctx, float lineHeight);++// Sets the text align of current text style, see NVGalign for options.+void nvgTextAlign(NVGcontext* ctx, int align);++// Sets the font face based on specified id of current text style.+void nvgFontFaceId(NVGcontext* ctx, int font);++// Sets the font face based on specified name of current text style.+void nvgFontFace(NVGcontext* ctx, const char* font);++// Draws text string at specified location. If end is specified only the sub-string up to the end is drawn.+float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end);++// Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn.+// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.+// Words longer than the max width are slit at nearest character (i.e. no hyphenation).+void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end);++// Measures the specified text string. Parameter bounds should be a pointer to float[4],+// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax]+// Returns the horizontal advance of the measured text (i.e. where the next character should drawn).+// Measured values are returned in local coordinate space.+float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds);++// Measures the specified multi-text string. Parameter bounds should be a pointer to float[4],+// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax]+// Measured values are returned in local coordinate space.+void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds);++// Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used.+// Measured values are returned in local coordinate space.+int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions);++// Returns the vertical metrics based on the current text style.+// Measured values are returned in local coordinate space.+void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh);++// Breaks the specified text into lines. If end is specified only the sub-string will be used.+// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.+// Words longer than the max width are slit at nearest character (i.e. no hyphenation).+int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows);++//+// Internal Render API+//+enum NVGtexture {+ NVG_TEXTURE_ALPHA = 0x01,+ NVG_TEXTURE_RGBA = 0x02,+};++struct NVGscissor {+ float xform[6];+ float extent[2];+};+typedef struct NVGscissor NVGscissor;++struct NVGvertex {+ float x,y,u,v;+};+typedef struct NVGvertex NVGvertex;++struct NVGpath {+ int first;+ int count;+ unsigned char closed;+ int nbevel;+ NVGvertex* fill;+ int nfill;+ NVGvertex* stroke;+ int nstroke;+ int winding;+ int convex;+};+typedef struct NVGpath NVGpath;++struct NVGparams {+ void* userPtr;+ int edgeAntiAlias;+ int (*renderCreate)(void* uptr);+ int (*renderCreateTexture)(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data);+ int (*renderDeleteTexture)(void* uptr, int image);+ int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data);+ int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h);+ void (*renderViewport)(void* uptr, int width, int height, float devicePixelRatio);+ void (*renderCancel)(void* uptr);+ void (*renderFlush)(void* uptr, NVGcompositeOperationState compositeOperation);+ void (*renderFill)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths);+ void (*renderStroke)(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths);+ void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGscissor* scissor, const NVGvertex* verts, int nverts);+ void (*renderDelete)(void* uptr);+};+typedef struct NVGparams NVGparams;++// Constructor and destructor, called by the render back-end.+NVGcontext* nvgCreateInternal(NVGparams* params);+void nvgDeleteInternal(NVGcontext* ctx);++NVGparams* nvgInternalParams(NVGcontext* ctx);++// Debug function to dump cached path data.+void nvgDebugDumpPathCache(NVGcontext* ctx);++#ifdef _MSC_VER+#pragma warning(pop)+#endif++#define NVG_NOTUSED(v) for (;;) { (void)(1 ? (void)0 : ( (void)(v) ) ); break; }++#ifdef __cplusplus+}+#endif++#endif // NANOVG_H
+ nanovg/src/nanovg_gl.h view
@@ -0,0 +1,1592 @@+//+// Copyright (c) 2009-2013 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.+//+#ifndef NANOVG_GL_H+#define NANOVG_GL_H++#ifdef __cplusplus+extern "C" {+#endif++// Create flags++enum NVGcreateFlags {+ // Flag indicating if geometry based anti-aliasing is used (may not be needed when using MSAA).+ NVG_ANTIALIAS = 1<<0,+ // Flag indicating if strokes should be drawn using stencil buffer. The rendering will be a little+ // slower, but path overlaps (i.e. self-intersecting or sharp turns) will be drawn just once.+ NVG_STENCIL_STROKES = 1<<1,+ // Flag indicating that additional debug checks are done.+ NVG_DEBUG = 1<<2,+};++#if defined NANOVG_GL2_IMPLEMENTATION+# define NANOVG_GL2 1+# define NANOVG_GL_IMPLEMENTATION 1+#elif defined NANOVG_GL3_IMPLEMENTATION+# define NANOVG_GL3 1+# define NANOVG_GL_IMPLEMENTATION 1+# define NANOVG_GL_USE_UNIFORMBUFFER 1+#elif defined NANOVG_GLES2_IMPLEMENTATION+# define NANOVG_GLES2 1+# define NANOVG_GL_IMPLEMENTATION 1+#elif defined NANOVG_GLES3_IMPLEMENTATION+# define NANOVG_GLES3 1+# define NANOVG_GL_IMPLEMENTATION 1+#endif++#define NANOVG_GL_USE_STATE_FILTER (1)++// Creates NanoVG contexts for different OpenGL (ES) versions.+// Flags should be combination of the create flags above.++#if defined NANOVG_GL2++NVGcontext* nvgCreateGL2(int flags);+void nvgDeleteGL2(NVGcontext* ctx);++int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);+GLuint nvglImageHandleGL2(NVGcontext* ctx, int image);++#endif++#if defined NANOVG_GL3++NVGcontext* nvgCreateGL3(int flags);+void nvgDeleteGL3(NVGcontext* ctx);++int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);+GLuint nvglImageHandleGL3(NVGcontext* ctx, int image);++#endif++#if defined NANOVG_GLES2++NVGcontext* nvgCreateGLES2(int flags);+void nvgDeleteGLES2(NVGcontext* ctx);++int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);+GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image);++#endif++#if defined NANOVG_GLES3++NVGcontext* nvgCreateGLES3(int flags);+void nvgDeleteGLES3(NVGcontext* ctx);++int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int flags);+GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image);++#endif++// These are additional flags on top of NVGimageFlags.+enum NVGimageFlagsGL {+ NVG_IMAGE_NODELETE = 1<<16, // Do not delete GL texture handle.+};++#ifdef __cplusplus+}+#endif++#endif /* NANOVG_GL_H */++#ifdef NANOVG_GL_IMPLEMENTATION++#include <stdlib.h>+#include <stdio.h>+#include <string.h>+#include <math.h>+#include "nanovg.h"++enum GLNVGuniformLoc {+ GLNVG_LOC_VIEWSIZE,+ GLNVG_LOC_TEX,+ GLNVG_LOC_FRAG,+ GLNVG_MAX_LOCS+};++enum GLNVGshaderType {+ NSVG_SHADER_FILLGRAD,+ NSVG_SHADER_FILLIMG,+ NSVG_SHADER_SIMPLE,+ NSVG_SHADER_IMG+};++#if NANOVG_GL_USE_UNIFORMBUFFER+enum GLNVGuniformBindings {+ GLNVG_FRAG_BINDING = 0,+};+#endif++struct GLNVGshader {+ GLuint prog;+ GLuint frag;+ GLuint vert;+ GLint loc[GLNVG_MAX_LOCS];+};+typedef struct GLNVGshader GLNVGshader;++struct GLNVGtexture {+ int id;+ GLuint tex;+ int width, height;+ int type;+ int flags;+};+typedef struct GLNVGtexture GLNVGtexture;++enum GLNVGcallType {+ GLNVG_NONE = 0,+ GLNVG_FILL,+ GLNVG_CONVEXFILL,+ GLNVG_STROKE,+ GLNVG_TRIANGLES,+};++struct GLNVGcall {+ int type;+ int image;+ int pathOffset;+ int pathCount;+ int triangleOffset;+ int triangleCount;+ int uniformOffset;+};+typedef struct GLNVGcall GLNVGcall;++struct GLNVGpath {+ int fillOffset;+ int fillCount;+ int strokeOffset;+ int strokeCount;+};+typedef struct GLNVGpath GLNVGpath;++struct GLNVGfragUniforms {+ #if NANOVG_GL_USE_UNIFORMBUFFER+ float scissorMat[12]; // matrices are actually 3 vec4s+ float paintMat[12];+ struct NVGcolor innerCol;+ struct NVGcolor outerCol;+ float scissorExt[2];+ float scissorScale[2];+ float extent[2];+ float radius;+ float feather;+ float strokeMult;+ float strokeThr;+ int texType;+ int type;+ #else+ // note: after modifying layout or size of uniform array,+ // don't forget to also update the fragment shader source!+ #define NANOVG_GL_UNIFORMARRAY_SIZE 11+ union {+ struct {+ float scissorMat[12]; // matrices are actually 3 vec4s+ float paintMat[12];+ struct NVGcolor innerCol;+ struct NVGcolor outerCol;+ float scissorExt[2];+ float scissorScale[2];+ float extent[2];+ float radius;+ float feather;+ float strokeMult;+ float strokeThr;+ float texType;+ float type;+ };+ float uniformArray[NANOVG_GL_UNIFORMARRAY_SIZE][4];+ };+ #endif+};+typedef struct GLNVGfragUniforms GLNVGfragUniforms;++struct GLNVGcontext {+ GLNVGshader shader;+ GLNVGtexture* textures;+ float view[2];+ int ntextures;+ int ctextures;+ int textureId;+ GLuint vertBuf;+#if defined NANOVG_GL3+ GLuint vertArr;+#endif+#if NANOVG_GL_USE_UNIFORMBUFFER+ GLuint fragBuf;+#endif+ int fragSize;+ int flags;++ // Per frame buffers+ GLNVGcall* calls;+ int ccalls;+ int ncalls;+ GLNVGpath* paths;+ int cpaths;+ int npaths;+ struct NVGvertex* verts;+ int cverts;+ int nverts;+ unsigned char* uniforms;+ int cuniforms;+ int nuniforms;++ // cached state+ #if NANOVG_GL_USE_STATE_FILTER+ GLuint boundTexture;+ GLuint stencilMask;+ GLenum stencilFunc;+ GLint stencilFuncRef;+ GLuint stencilFuncMask;+ #endif+};+typedef struct GLNVGcontext GLNVGcontext;++static int glnvg__maxi(int a, int b) { return a > b ? a : b; }++#ifdef NANOVG_GLES2+static unsigned int glnvg__nearestPow2(unsigned int num)+{+ unsigned n = num > 0 ? num - 1 : 0;+ n |= n >> 1;+ n |= n >> 2;+ n |= n >> 4;+ n |= n >> 8;+ n |= n >> 16;+ n++;+ return n;+}+#endif++static void glnvg__bindTexture(GLNVGcontext* gl, GLuint tex)+{+#if NANOVG_GL_USE_STATE_FILTER+ if (gl->boundTexture != tex) {+ gl->boundTexture = tex;+ glBindTexture(GL_TEXTURE_2D, tex);+ }+#else+ glBindTexture(GL_TEXTURE_2D, tex);+#endif+}++static void glnvg__stencilMask(GLNVGcontext* gl, GLuint mask)+{+#if NANOVG_GL_USE_STATE_FILTER+ if (gl->stencilMask != mask) {+ gl->stencilMask = mask;+ glStencilMask(mask);+ }+#else+ glStencilMask(mask);+#endif+}++static void glnvg__stencilFunc(GLNVGcontext* gl, GLenum func, GLint ref, GLuint mask)+{+#if NANOVG_GL_USE_STATE_FILTER+ if ((gl->stencilFunc != func) ||+ (gl->stencilFuncRef != ref) ||+ (gl->stencilFuncMask != mask)) {++ gl->stencilFunc = func;+ gl->stencilFuncRef = ref;+ gl->stencilFuncMask = mask;+ glStencilFunc(func, ref, mask);+ }+#else+ glStencilFunc(func, ref, mask);+#endif+}++static GLNVGtexture* glnvg__allocTexture(GLNVGcontext* gl)+{+ GLNVGtexture* tex = NULL;+ int i;++ for (i = 0; i < gl->ntextures; i++) {+ if (gl->textures[i].id == 0) {+ tex = &gl->textures[i];+ break;+ }+ }+ if (tex == NULL) {+ if (gl->ntextures+1 > gl->ctextures) {+ GLNVGtexture* textures;+ int ctextures = glnvg__maxi(gl->ntextures+1, 4) + gl->ctextures/2; // 1.5x Overallocate+ textures = (GLNVGtexture*)realloc(gl->textures, sizeof(GLNVGtexture)*ctextures);+ if (textures == NULL) return NULL;+ gl->textures = textures;+ gl->ctextures = ctextures;+ }+ tex = &gl->textures[gl->ntextures++];+ }++ memset(tex, 0, sizeof(*tex));+ tex->id = ++gl->textureId;++ return tex;+}++static GLNVGtexture* glnvg__findTexture(GLNVGcontext* gl, int id)+{+ int i;+ for (i = 0; i < gl->ntextures; i++)+ if (gl->textures[i].id == id)+ return &gl->textures[i];+ return NULL;+}++static int glnvg__deleteTexture(GLNVGcontext* gl, int id)+{+ int i;+ for (i = 0; i < gl->ntextures; i++) {+ if (gl->textures[i].id == id) {+ if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0)+ glDeleteTextures(1, &gl->textures[i].tex);+ memset(&gl->textures[i], 0, sizeof(gl->textures[i]));+ return 1;+ }+ }+ return 0;+}++static void glnvg__dumpShaderError(GLuint shader, const char* name, const char* type)+{+ GLchar str[512+1];+ GLsizei len = 0;+ glGetShaderInfoLog(shader, 512, &len, str);+ if (len > 512) len = 512;+ str[len] = '\0';+ printf("Shader %s/%s error:\n%s\n", name, type, str);+}++static void glnvg__dumpProgramError(GLuint prog, const char* name)+{+ GLchar str[512+1];+ GLsizei len = 0;+ glGetProgramInfoLog(prog, 512, &len, str);+ if (len > 512) len = 512;+ str[len] = '\0';+ printf("Program %s error:\n%s\n", name, str);+}++static void glnvg__checkError(GLNVGcontext* gl, const char* str)+{+ GLenum err;+ if ((gl->flags & NVG_DEBUG) == 0) return;+ err = glGetError();+ if (err != GL_NO_ERROR) {+ printf("Error %08x after %s\n", err, str);+ return;+ }+}++static int glnvg__createShader(GLNVGshader* shader, const char* name, const char* header, const char* opts, const char* vshader, const char* fshader)+{+ GLint status;+ GLuint prog, vert, frag;+ const char* str[3];+ str[0] = header;+ str[1] = opts != NULL ? opts : "";++ memset(shader, 0, sizeof(*shader));++ prog = glCreateProgram();+ vert = glCreateShader(GL_VERTEX_SHADER);+ frag = glCreateShader(GL_FRAGMENT_SHADER);+ str[2] = vshader;+ glShaderSource(vert, 3, str, 0);+ str[2] = fshader;+ glShaderSource(frag, 3, str, 0);++ glCompileShader(vert);+ glGetShaderiv(vert, GL_COMPILE_STATUS, &status);+ if (status != GL_TRUE) {+ glnvg__dumpShaderError(vert, name, "vert");+ return 0;+ }++ glCompileShader(frag);+ glGetShaderiv(frag, GL_COMPILE_STATUS, &status);+ if (status != GL_TRUE) {+ glnvg__dumpShaderError(frag, name, "frag");+ return 0;+ }++ glAttachShader(prog, vert);+ glAttachShader(prog, frag);++ glBindAttribLocation(prog, 0, "vertex");+ glBindAttribLocation(prog, 1, "tcoord");++ glLinkProgram(prog);+ glGetProgramiv(prog, GL_LINK_STATUS, &status);+ if (status != GL_TRUE) {+ glnvg__dumpProgramError(prog, name);+ return 0;+ }++ shader->prog = prog;+ shader->vert = vert;+ shader->frag = frag;++ return 1;+}++static void glnvg__deleteShader(GLNVGshader* shader)+{+ if (shader->prog != 0)+ glDeleteProgram(shader->prog);+ if (shader->vert != 0)+ glDeleteShader(shader->vert);+ if (shader->frag != 0)+ glDeleteShader(shader->frag);+}++static void glnvg__getUniforms(GLNVGshader* shader)+{+ shader->loc[GLNVG_LOC_VIEWSIZE] = glGetUniformLocation(shader->prog, "viewSize");+ shader->loc[GLNVG_LOC_TEX] = glGetUniformLocation(shader->prog, "tex");++#if NANOVG_GL_USE_UNIFORMBUFFER+ shader->loc[GLNVG_LOC_FRAG] = glGetUniformBlockIndex(shader->prog, "frag");+#else+ shader->loc[GLNVG_LOC_FRAG] = glGetUniformLocation(shader->prog, "frag");+#endif+}++static int glnvg__renderCreate(void* uptr)+{+ GLNVGcontext* gl = (GLNVGcontext*)uptr;+ int align = 4;++ // TODO: mediump float may not be enough for GLES2 in iOS.+ // see the following discussion: https://github.com/memononen/nanovg/issues/46+ static const char* shaderHeader =+#if defined NANOVG_GL2+ "#define NANOVG_GL2 1\n"+#elif defined NANOVG_GL3+ "#version 150 core\n"+ "#define NANOVG_GL3 1\n"+#elif defined NANOVG_GLES2+ "#version 100\n"+ "#define NANOVG_GL2 1\n"+#elif defined NANOVG_GLES3+ "#version 300 es\n"+ "#define NANOVG_GL3 1\n"+#endif++#if NANOVG_GL_USE_UNIFORMBUFFER+ "#define USE_UNIFORMBUFFER 1\n"+#else+ "#define UNIFORMARRAY_SIZE 11\n"+#endif+ "\n";++ static const char* fillVertShader =+ "#ifdef NANOVG_GL3\n"+ " uniform vec2 viewSize;\n"+ " in vec2 vertex;\n"+ " in vec2 tcoord;\n"+ " out vec2 ftcoord;\n"+ " out vec2 fpos;\n"+ "#else\n"+ " uniform vec2 viewSize;\n"+ " attribute vec2 vertex;\n"+ " attribute vec2 tcoord;\n"+ " varying vec2 ftcoord;\n"+ " varying vec2 fpos;\n"+ "#endif\n"+ "void main(void) {\n"+ " ftcoord = tcoord;\n"+ " fpos = vertex;\n"+ " gl_Position = vec4(2.0*vertex.x/viewSize.x - 1.0, 1.0 - 2.0*vertex.y/viewSize.y, 0, 1);\n"+ "}\n";++ static const char* fillFragShader =+ "#ifdef GL_ES\n"+ "#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n"+ " precision highp float;\n"+ "#else\n"+ " precision mediump float;\n"+ "#endif\n"+ "#endif\n"+ "#ifdef NANOVG_GL3\n"+ "#ifdef USE_UNIFORMBUFFER\n"+ " layout(std140) uniform frag {\n"+ " mat3 scissorMat;\n"+ " mat3 paintMat;\n"+ " vec4 innerCol;\n"+ " vec4 outerCol;\n"+ " vec2 scissorExt;\n"+ " vec2 scissorScale;\n"+ " vec2 extent;\n"+ " float radius;\n"+ " float feather;\n"+ " float strokeMult;\n"+ " float strokeThr;\n"+ " int texType;\n"+ " int type;\n"+ " };\n"+ "#else\n" // NANOVG_GL3 && !USE_UNIFORMBUFFER+ " uniform vec4 frag[UNIFORMARRAY_SIZE];\n"+ "#endif\n"+ " uniform sampler2D tex;\n"+ " in vec2 ftcoord;\n"+ " in vec2 fpos;\n"+ " out vec4 outColor;\n"+ "#else\n" // !NANOVG_GL3+ " uniform vec4 frag[UNIFORMARRAY_SIZE];\n"+ " uniform sampler2D tex;\n"+ " varying vec2 ftcoord;\n"+ " varying vec2 fpos;\n"+ "#endif\n"+ "#ifndef USE_UNIFORMBUFFER\n"+ " #define scissorMat mat3(frag[0].xyz, frag[1].xyz, frag[2].xyz)\n"+ " #define paintMat mat3(frag[3].xyz, frag[4].xyz, frag[5].xyz)\n"+ " #define innerCol frag[6]\n"+ " #define outerCol frag[7]\n"+ " #define scissorExt frag[8].xy\n"+ " #define scissorScale frag[8].zw\n"+ " #define extent frag[9].xy\n"+ " #define radius frag[9].z\n"+ " #define feather frag[9].w\n"+ " #define strokeMult frag[10].x\n"+ " #define strokeThr frag[10].y\n"+ " #define texType int(frag[10].z)\n"+ " #define type int(frag[10].w)\n"+ "#endif\n"+ "\n"+ "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n"+ " vec2 ext2 = ext - vec2(rad,rad);\n"+ " vec2 d = abs(pt) - ext2;\n"+ " return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n"+ "}\n"+ "\n"+ "// Scissoring\n"+ "float scissorMask(vec2 p) {\n"+ " vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);\n"+ " sc = vec2(0.5,0.5) - sc * scissorScale;\n"+ " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n"+ "}\n"+ "#ifdef EDGE_AA\n"+ "// Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n"+ "float strokeMask() {\n"+ " return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult) * min(1.0, ftcoord.y);\n"+ "}\n"+ "#endif\n"+ "\n"+ "void main(void) {\n"+ " vec4 result;\n"+ " float scissor = scissorMask(fpos);\n"+ "#ifdef EDGE_AA\n"+ " float strokeAlpha = strokeMask();\n"+ "#else\n"+ " float strokeAlpha = 1.0;\n"+ "#endif\n"+ " if (type == 0) { // Gradient\n"+ " // Calculate gradient color using box gradient\n"+ " vec2 pt = (paintMat * vec3(fpos,1.0)).xy;\n"+ " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n"+ " vec4 color = mix(innerCol,outerCol,d);\n"+ " // Combine alpha\n"+ " color *= strokeAlpha * scissor;\n"+ " result = color;\n"+ " } else if (type == 1) { // Image\n"+ " // Calculate color fron texture\n"+ " vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;\n"+ "#ifdef NANOVG_GL3\n"+ " vec4 color = texture(tex, pt);\n"+ "#else\n"+ " vec4 color = texture2D(tex, pt);\n"+ "#endif\n"+ " if (texType == 1) color = vec4(color.xyz*color.w,color.w);"+ " if (texType == 2) color = vec4(color.x);"+ " // Apply color tint and alpha.\n"+ " color *= innerCol;\n"+ " // Combine alpha\n"+ " color *= strokeAlpha * scissor;\n"+ " result = color;\n"+ " } else if (type == 2) { // Stencil fill\n"+ " result = vec4(1,1,1,1);\n"+ " } else if (type == 3) { // Textured tris\n"+ "#ifdef NANOVG_GL3\n"+ " vec4 color = texture(tex, ftcoord);\n"+ "#else\n"+ " vec4 color = texture2D(tex, ftcoord);\n"+ "#endif\n"+ " if (texType == 1) color = vec4(color.xyz*color.w,color.w);"+ " if (texType == 2) color = vec4(color.x);"+ " color *= scissor;\n"+ " result = color * innerCol;\n"+ " }\n"+ "#ifdef EDGE_AA\n"+ " if (strokeAlpha < strokeThr) discard;\n"+ "#endif\n"+ "#ifdef NANOVG_GL3\n"+ " outColor = result;\n"+ "#else\n"+ " gl_FragColor = result;\n"+ "#endif\n"+ "}\n";++ glnvg__checkError(gl, "init");++ if (gl->flags & NVG_ANTIALIAS) {+ if (glnvg__createShader(&gl->shader, "shader", shaderHeader, "#define EDGE_AA 1\n", fillVertShader, fillFragShader) == 0)+ return 0;+ } else {+ if (glnvg__createShader(&gl->shader, "shader", shaderHeader, NULL, fillVertShader, fillFragShader) == 0)+ return 0;+ }++ glnvg__checkError(gl, "uniform locations");+ glnvg__getUniforms(&gl->shader);++ // Create dynamic vertex array+#if defined NANOVG_GL3+ glGenVertexArrays(1, &gl->vertArr);+#endif+ glGenBuffers(1, &gl->vertBuf);++#if NANOVG_GL_USE_UNIFORMBUFFER+ // Create UBOs+ glUniformBlockBinding(gl->shader.prog, gl->shader.loc[GLNVG_LOC_FRAG], GLNVG_FRAG_BINDING);+ glGenBuffers(1, &gl->fragBuf);+ glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &align);+#endif+ gl->fragSize = sizeof(GLNVGfragUniforms) + align - sizeof(GLNVGfragUniforms) % align;++ glnvg__checkError(gl, "create done");++ glFinish();++ return 1;+}++static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data)+{+ GLNVGcontext* gl = (GLNVGcontext*)uptr;+ GLNVGtexture* tex = glnvg__allocTexture(gl);++ if (tex == NULL) return 0;++#ifdef NANOVG_GLES2+ // Check for non-power of 2.+ if (glnvg__nearestPow2(w) != (unsigned int)w || glnvg__nearestPow2(h) != (unsigned int)h) {+ // No repeat+ if ((imageFlags & NVG_IMAGE_REPEATX) != 0 || (imageFlags & NVG_IMAGE_REPEATY) != 0) {+ printf("Repeat X/Y is not supported for non power-of-two textures (%d x %d)\n", w, h);+ imageFlags &= ~(NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);+ }+ // No mips.+ if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) {+ printf("Mip-maps is not support for non power-of-two textures (%d x %d)\n", w, h);+ imageFlags &= ~NVG_IMAGE_GENERATE_MIPMAPS;+ }+ }+#endif++ glGenTextures(1, &tex->tex);+ tex->width = w;+ tex->height = h;+ tex->type = type;+ tex->flags = imageFlags;+ glnvg__bindTexture(gl, tex->tex);++ glPixelStorei(GL_UNPACK_ALIGNMENT,1);+#ifndef NANOVG_GLES2+ glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width);+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);+ glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);+#endif++#if defined (NANOVG_GL2)+ // GL 1.4 and later has support for generating mipmaps using a tex parameter.+ if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) {+ glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);+ }+#endif++ if (type == NVG_TEXTURE_RGBA)+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);+ else+#if defined(NANOVG_GLES2)+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);+#elif defined(NANOVG_GLES3)+ glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data);+#else+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, data);+#endif++ if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) {+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);+ } else {+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);+ }+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);++ if (imageFlags & NVG_IMAGE_REPEATX)+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);+ else+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);++ if (imageFlags & NVG_IMAGE_REPEATY)+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);+ else+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);++ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);+#ifndef NANOVG_GLES2+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);+ glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);+#endif++ // The new way to build mipmaps on GLES and GL3+#if !defined(NANOVG_GL2)+ if (imageFlags & NVG_IMAGE_GENERATE_MIPMAPS) {+ glGenerateMipmap(GL_TEXTURE_2D);+ }+#endif++ glnvg__checkError(gl, "create tex");+ glnvg__bindTexture(gl, 0);++ return tex->id;+}+++static int glnvg__renderDeleteTexture(void* uptr, int image)+{+ GLNVGcontext* gl = (GLNVGcontext*)uptr;+ return glnvg__deleteTexture(gl, image);+}++static int glnvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data)+{+ GLNVGcontext* gl = (GLNVGcontext*)uptr;+ GLNVGtexture* tex = glnvg__findTexture(gl, image);++ if (tex == NULL) return 0;+ glnvg__bindTexture(gl, tex->tex);++ glPixelStorei(GL_UNPACK_ALIGNMENT,1);++#ifndef NANOVG_GLES2+ glPixelStorei(GL_UNPACK_ROW_LENGTH, tex->width);+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, x);+ glPixelStorei(GL_UNPACK_SKIP_ROWS, y);+#else+ // No support for all of skip, need to update a whole row at a time.+ if (tex->type == NVG_TEXTURE_RGBA)+ data += y*tex->width*4;+ else+ data += y*tex->width;+ x = 0;+ w = tex->width;+#endif++ if (tex->type == NVG_TEXTURE_RGBA)+ glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RGBA, GL_UNSIGNED_BYTE, data);+ else+#ifdef NANOVG_GLES2+ glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_LUMINANCE, GL_UNSIGNED_BYTE, data);+#else+ glTexSubImage2D(GL_TEXTURE_2D, 0, x,y, w,h, GL_RED, GL_UNSIGNED_BYTE, data);+#endif++ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);+#ifndef NANOVG_GLES2+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);+ glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);+#endif++ glnvg__bindTexture(gl, 0);++ return 1;+}++static int glnvg__renderGetTextureSize(void* uptr, int image, int* w, int* h)+{+ GLNVGcontext* gl = (GLNVGcontext*)uptr;+ GLNVGtexture* tex = glnvg__findTexture(gl, image);+ if (tex == NULL) return 0;+ *w = tex->width;+ *h = tex->height;+ return 1;+}++static void glnvg__xformToMat3x4(float* m3, float* t)+{+ m3[0] = t[0];+ m3[1] = t[1];+ m3[2] = 0.0f;+ m3[3] = 0.0f;+ m3[4] = t[2];+ m3[5] = t[3];+ m3[6] = 0.0f;+ m3[7] = 0.0f;+ m3[8] = t[4];+ m3[9] = t[5];+ m3[10] = 1.0f;+ m3[11] = 0.0f;+}++static NVGcolor glnvg__premulColor(NVGcolor c)+{+ c.r *= c.a;+ c.g *= c.a;+ c.b *= c.a;+ return c;+}++static int glnvg__convertPaint(GLNVGcontext* gl, GLNVGfragUniforms* frag, NVGpaint* paint,+ NVGscissor* scissor, float width, float fringe, float strokeThr)+{+ GLNVGtexture* tex = NULL;+ float invxform[6];++ memset(frag, 0, sizeof(*frag));++ frag->innerCol = glnvg__premulColor(paint->innerColor);+ frag->outerCol = glnvg__premulColor(paint->outerColor);++ if (scissor->extent[0] < -0.5f || scissor->extent[1] < -0.5f) {+ memset(frag->scissorMat, 0, sizeof(frag->scissorMat));+ frag->scissorExt[0] = 1.0f;+ frag->scissorExt[1] = 1.0f;+ frag->scissorScale[0] = 1.0f;+ frag->scissorScale[1] = 1.0f;+ } else {+ nvgTransformInverse(invxform, scissor->xform);+ glnvg__xformToMat3x4(frag->scissorMat, invxform);+ frag->scissorExt[0] = scissor->extent[0];+ frag->scissorExt[1] = scissor->extent[1];+ frag->scissorScale[0] = sqrtf(scissor->xform[0]*scissor->xform[0] + scissor->xform[2]*scissor->xform[2]) / fringe;+ frag->scissorScale[1] = sqrtf(scissor->xform[1]*scissor->xform[1] + scissor->xform[3]*scissor->xform[3]) / fringe;+ }++ memcpy(frag->extent, paint->extent, sizeof(frag->extent));+ frag->strokeMult = (width*0.5f + fringe*0.5f) / fringe;+ frag->strokeThr = strokeThr;++ if (paint->image != 0) {+ tex = glnvg__findTexture(gl, paint->image);+ if (tex == NULL) return 0;+ if ((tex->flags & NVG_IMAGE_FLIPY) != 0) {+ float m1[6], m2[6];+ nvgTransformTranslate(m1, 0.0f, frag->extent[1] * 0.5f);+ nvgTransformMultiply(m1, paint->xform);+ nvgTransformScale(m2, 1.0f, -1.0f);+ nvgTransformMultiply(m2, m1);+ nvgTransformTranslate(m1, 0.0f, -frag->extent[1] * 0.5f);+ nvgTransformMultiply(m1, m2);+ nvgTransformInverse(invxform, m1);+ } else {+ nvgTransformInverse(invxform, paint->xform);+ }+ frag->type = NSVG_SHADER_FILLIMG;++ if (tex->type == NVG_TEXTURE_RGBA)+ frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1;+ else+ frag->texType = 2;+// printf("frag->texType = %d\n", frag->texType);+ } else {+ frag->type = NSVG_SHADER_FILLGRAD;+ frag->radius = paint->radius;+ frag->feather = paint->feather;+ nvgTransformInverse(invxform, paint->xform);+ }++ glnvg__xformToMat3x4(frag->paintMat, invxform);++ return 1;+}++static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i);++static void glnvg__setUniforms(GLNVGcontext* gl, int uniformOffset, int image)+{+#if NANOVG_GL_USE_UNIFORMBUFFER+ glBindBufferRange(GL_UNIFORM_BUFFER, GLNVG_FRAG_BINDING, gl->fragBuf, uniformOffset, sizeof(GLNVGfragUniforms));+#else+ GLNVGfragUniforms* frag = nvg__fragUniformPtr(gl, uniformOffset);+ glUniform4fv(gl->shader.loc[GLNVG_LOC_FRAG], NANOVG_GL_UNIFORMARRAY_SIZE, &(frag->uniformArray[0][0]));+#endif++ if (image != 0) {+ GLNVGtexture* tex = glnvg__findTexture(gl, image);+ glnvg__bindTexture(gl, tex != NULL ? tex->tex : 0);+ glnvg__checkError(gl, "tex paint tex");+ } else {+ glnvg__bindTexture(gl, 0);+ }+}++static void glnvg__renderViewport(void* uptr, int width, int height, float devicePixelRatio)+{+ GLNVGcontext* gl = (GLNVGcontext*)uptr;+ gl->view[0] = (float)width;+ gl->view[1] = (float)height;+}++static void glnvg__fill(GLNVGcontext* gl, GLNVGcall* call)+{+ GLNVGpath* paths = &gl->paths[call->pathOffset];+ int i, npaths = call->pathCount;++ // Draw shapes+ glEnable(GL_STENCIL_TEST);+ glnvg__stencilMask(gl, 0xff);+ glnvg__stencilFunc(gl, GL_ALWAYS, 0, 0xff);+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);++ // set bindpoint for solid loc+ glnvg__setUniforms(gl, call->uniformOffset, 0);+ glnvg__checkError(gl, "fill simple");++ glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP);+ glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP);+ glDisable(GL_CULL_FACE);+ for (i = 0; i < npaths; i++)+ glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);+ glEnable(GL_CULL_FACE);++ // Draw anti-aliased pixels+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);++ glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image);+ glnvg__checkError(gl, "fill fill");++ if (gl->flags & NVG_ANTIALIAS) {+ glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff);+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);+ // Draw fringes+ for (i = 0; i < npaths; i++)+ glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);+ }++ // Draw fill+ glnvg__stencilFunc(gl, GL_NOTEQUAL, 0x0, 0xff);+ glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);+ glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount);++ glDisable(GL_STENCIL_TEST);+}++static void glnvg__convexFill(GLNVGcontext* gl, GLNVGcall* call)+{+ GLNVGpath* paths = &gl->paths[call->pathOffset];+ int i, npaths = call->pathCount;++ glnvg__setUniforms(gl, call->uniformOffset, call->image);+ glnvg__checkError(gl, "convex fill");++ for (i = 0; i < npaths; i++)+ glDrawArrays(GL_TRIANGLE_FAN, paths[i].fillOffset, paths[i].fillCount);+ if (gl->flags & NVG_ANTIALIAS) {+ // Draw fringes+ for (i = 0; i < npaths; i++)+ glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);+ }+}++static void glnvg__stroke(GLNVGcontext* gl, GLNVGcall* call)+{+ GLNVGpath* paths = &gl->paths[call->pathOffset];+ int npaths = call->pathCount, i;++ if (gl->flags & NVG_STENCIL_STROKES) {++ glEnable(GL_STENCIL_TEST);+ glnvg__stencilMask(gl, 0xff);++ // Fill the stroke base without overlap+ glnvg__stencilFunc(gl, GL_EQUAL, 0x0, 0xff);+ glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);+ glnvg__setUniforms(gl, call->uniformOffset + gl->fragSize, call->image);+ glnvg__checkError(gl, "stroke fill 0");+ for (i = 0; i < npaths; i++)+ glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);++ // Draw anti-aliased pixels.+ glnvg__setUniforms(gl, call->uniformOffset, call->image);+ glnvg__stencilFunc(gl, GL_EQUAL, 0x00, 0xff);+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);+ for (i = 0; i < npaths; i++)+ glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);++ // Clear stencil buffer.+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);+ glnvg__stencilFunc(gl, GL_ALWAYS, 0x0, 0xff);+ glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO);+ glnvg__checkError(gl, "stroke fill 1");+ for (i = 0; i < npaths; i++)+ glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);++ glDisable(GL_STENCIL_TEST);++// glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f);++ } else {+ glnvg__setUniforms(gl, call->uniformOffset, call->image);+ glnvg__checkError(gl, "stroke fill");+ // Draw Strokes+ for (i = 0; i < npaths; i++)+ glDrawArrays(GL_TRIANGLE_STRIP, paths[i].strokeOffset, paths[i].strokeCount);+ }+}++static void glnvg__triangles(GLNVGcontext* gl, GLNVGcall* call)+{+ glnvg__setUniforms(gl, call->uniformOffset, call->image);+ glnvg__checkError(gl, "triangles fill");++ glDrawArrays(GL_TRIANGLES, call->triangleOffset, call->triangleCount);+}++static void glnvg__renderCancel(void* uptr) {+ GLNVGcontext* gl = (GLNVGcontext*)uptr;+ gl->nverts = 0;+ gl->npaths = 0;+ gl->ncalls = 0;+ gl->nuniforms = 0;+}++static GLenum glnvg_convertBlendFuncFactor(int factor)+{+ if (factor == NVG_ZERO)+ return GL_ZERO;+ if (factor == NVG_ONE)+ return GL_ONE;+ if (factor == NVG_SRC_COLOR)+ return GL_SRC_COLOR;+ if (factor == NVG_ONE_MINUS_SRC_COLOR)+ return GL_ONE_MINUS_SRC_COLOR;+ if (factor == NVG_DST_COLOR)+ return GL_DST_COLOR;+ if (factor == NVG_ONE_MINUS_DST_COLOR)+ return GL_ONE_MINUS_DST_COLOR;+ if (factor == NVG_SRC_ALPHA)+ return GL_SRC_ALPHA;+ if (factor == NVG_ONE_MINUS_SRC_ALPHA)+ return GL_ONE_MINUS_SRC_ALPHA;+ if (factor == NVG_DST_ALPHA)+ return GL_DST_ALPHA;+ if (factor == NVG_ONE_MINUS_DST_ALPHA)+ return GL_ONE_MINUS_DST_ALPHA;+ if (factor == NVG_SRC_ALPHA_SATURATE)+ return GL_SRC_ALPHA_SATURATE;+ return GL_INVALID_ENUM;+}++static void glnvg__blendCompositeOperation(NVGcompositeOperationState op)+{+ GLenum srcRGB = glnvg_convertBlendFuncFactor(op.srcRGB);+ GLenum dstRGB = glnvg_convertBlendFuncFactor(op.dstRGB);+ GLenum srcAlpha = glnvg_convertBlendFuncFactor(op.srcAlpha);+ GLenum dstAlpha = glnvg_convertBlendFuncFactor(op.dstAlpha);+ if (srcRGB == GL_INVALID_ENUM || dstRGB == GL_INVALID_ENUM || srcAlpha == GL_INVALID_ENUM || dstAlpha == GL_INVALID_ENUM)+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);+ else+ glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha);+}++static void glnvg__renderFlush(void* uptr, NVGcompositeOperationState compositeOperation)+{+ GLNVGcontext* gl = (GLNVGcontext*)uptr;+ int i;++ if (gl->ncalls > 0) {++ // Setup require GL state.+ glUseProgram(gl->shader.prog);++ glnvg__blendCompositeOperation(compositeOperation);+ glEnable(GL_CULL_FACE);+ glCullFace(GL_BACK);+ glFrontFace(GL_CCW);+ glEnable(GL_BLEND);+ glDisable(GL_DEPTH_TEST);+ glDisable(GL_SCISSOR_TEST);+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);+ glStencilMask(0xffffffff);+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);+ glStencilFunc(GL_ALWAYS, 0, 0xffffffff);+ glActiveTexture(GL_TEXTURE0);+ glBindTexture(GL_TEXTURE_2D, 0);+ #if NANOVG_GL_USE_STATE_FILTER+ gl->boundTexture = 0;+ gl->stencilMask = 0xffffffff;+ gl->stencilFunc = GL_ALWAYS;+ gl->stencilFuncRef = 0;+ gl->stencilFuncMask = 0xffffffff;+ #endif++#if NANOVG_GL_USE_UNIFORMBUFFER+ // Upload ubo for frag shaders+ glBindBuffer(GL_UNIFORM_BUFFER, gl->fragBuf);+ glBufferData(GL_UNIFORM_BUFFER, gl->nuniforms * gl->fragSize, gl->uniforms, GL_STREAM_DRAW);+#endif++ // Upload vertex data+#if defined NANOVG_GL3+ glBindVertexArray(gl->vertArr);+#endif+ glBindBuffer(GL_ARRAY_BUFFER, gl->vertBuf);+ glBufferData(GL_ARRAY_BUFFER, gl->nverts * sizeof(NVGvertex), gl->verts, GL_STREAM_DRAW);+ glEnableVertexAttribArray(0);+ glEnableVertexAttribArray(1);+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(NVGvertex), (const GLvoid*)(size_t)0);+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(NVGvertex), (const GLvoid*)(0 + 2*sizeof(float)));++ // Set view and texture just once per frame.+ glUniform1i(gl->shader.loc[GLNVG_LOC_TEX], 0);+ glUniform2fv(gl->shader.loc[GLNVG_LOC_VIEWSIZE], 1, gl->view);++#if NANOVG_GL_USE_UNIFORMBUFFER+ glBindBuffer(GL_UNIFORM_BUFFER, gl->fragBuf);+#endif++ for (i = 0; i < gl->ncalls; i++) {+ GLNVGcall* call = &gl->calls[i];+ if (call->type == GLNVG_FILL)+ glnvg__fill(gl, call);+ else if (call->type == GLNVG_CONVEXFILL)+ glnvg__convexFill(gl, call);+ else if (call->type == GLNVG_STROKE)+ glnvg__stroke(gl, call);+ else if (call->type == GLNVG_TRIANGLES)+ glnvg__triangles(gl, call);+ }++ glDisableVertexAttribArray(0);+ glDisableVertexAttribArray(1);+#if defined NANOVG_GL3+ glBindVertexArray(0);+#endif+ glDisable(GL_CULL_FACE);+ glBindBuffer(GL_ARRAY_BUFFER, 0);+ glUseProgram(0);+ glnvg__bindTexture(gl, 0);+ }++ // Reset calls+ gl->nverts = 0;+ gl->npaths = 0;+ gl->ncalls = 0;+ gl->nuniforms = 0;+}++static int glnvg__maxVertCount(const NVGpath* paths, int npaths)+{+ int i, count = 0;+ for (i = 0; i < npaths; i++) {+ count += paths[i].nfill;+ count += paths[i].nstroke;+ }+ return count;+}++static GLNVGcall* glnvg__allocCall(GLNVGcontext* gl)+{+ GLNVGcall* ret = NULL;+ if (gl->ncalls+1 > gl->ccalls) {+ GLNVGcall* calls;+ int ccalls = glnvg__maxi(gl->ncalls+1, 128) + gl->ccalls/2; // 1.5x Overallocate+ calls = (GLNVGcall*)realloc(gl->calls, sizeof(GLNVGcall) * ccalls);+ if (calls == NULL) return NULL;+ gl->calls = calls;+ gl->ccalls = ccalls;+ }+ ret = &gl->calls[gl->ncalls++];+ memset(ret, 0, sizeof(GLNVGcall));+ return ret;+}++static int glnvg__allocPaths(GLNVGcontext* gl, int n)+{+ int ret = 0;+ if (gl->npaths+n > gl->cpaths) {+ GLNVGpath* paths;+ int cpaths = glnvg__maxi(gl->npaths + n, 128) + gl->cpaths/2; // 1.5x Overallocate+ paths = (GLNVGpath*)realloc(gl->paths, sizeof(GLNVGpath) * cpaths);+ if (paths == NULL) return -1;+ gl->paths = paths;+ gl->cpaths = cpaths;+ }+ ret = gl->npaths;+ gl->npaths += n;+ return ret;+}++static int glnvg__allocVerts(GLNVGcontext* gl, int n)+{+ int ret = 0;+ if (gl->nverts+n > gl->cverts) {+ NVGvertex* verts;+ int cverts = glnvg__maxi(gl->nverts + n, 4096) + gl->cverts/2; // 1.5x Overallocate+ verts = (NVGvertex*)realloc(gl->verts, sizeof(NVGvertex) * cverts);+ if (verts == NULL) return -1;+ gl->verts = verts;+ gl->cverts = cverts;+ }+ ret = gl->nverts;+ gl->nverts += n;+ return ret;+}++static int glnvg__allocFragUniforms(GLNVGcontext* gl, int n)+{+ int ret = 0, structSize = gl->fragSize;+ if (gl->nuniforms+n > gl->cuniforms) {+ unsigned char* uniforms;+ int cuniforms = glnvg__maxi(gl->nuniforms+n, 128) + gl->cuniforms/2; // 1.5x Overallocate+ uniforms = (unsigned char*)realloc(gl->uniforms, structSize * cuniforms);+ if (uniforms == NULL) return -1;+ gl->uniforms = uniforms;+ gl->cuniforms = cuniforms;+ }+ ret = gl->nuniforms * structSize;+ gl->nuniforms += n;+ return ret;+}++static GLNVGfragUniforms* nvg__fragUniformPtr(GLNVGcontext* gl, int i)+{+ return (GLNVGfragUniforms*)&gl->uniforms[i];+}++static void glnvg__vset(NVGvertex* vtx, float x, float y, float u, float v)+{+ vtx->x = x;+ vtx->y = y;+ vtx->u = u;+ vtx->v = v;+}++static void glnvg__renderFill(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe,+ const float* bounds, const NVGpath* paths, int npaths)+{+ GLNVGcontext* gl = (GLNVGcontext*)uptr;+ GLNVGcall* call = glnvg__allocCall(gl);+ NVGvertex* quad;+ GLNVGfragUniforms* frag;+ int i, maxverts, offset;++ if (call == NULL) return;++ call->type = GLNVG_FILL;+ call->pathOffset = glnvg__allocPaths(gl, npaths);+ if (call->pathOffset == -1) goto error;+ call->pathCount = npaths;+ call->image = paint->image;++ if (npaths == 1 && paths[0].convex)+ call->type = GLNVG_CONVEXFILL;++ // Allocate vertices for all the paths.+ maxverts = glnvg__maxVertCount(paths, npaths) + 6;+ offset = glnvg__allocVerts(gl, maxverts);+ if (offset == -1) goto error;++ for (i = 0; i < npaths; i++) {+ GLNVGpath* copy = &gl->paths[call->pathOffset + i];+ const NVGpath* path = &paths[i];+ memset(copy, 0, sizeof(GLNVGpath));+ if (path->nfill > 0) {+ copy->fillOffset = offset;+ copy->fillCount = path->nfill;+ memcpy(&gl->verts[offset], path->fill, sizeof(NVGvertex) * path->nfill);+ offset += path->nfill;+ }+ if (path->nstroke > 0) {+ copy->strokeOffset = offset;+ copy->strokeCount = path->nstroke;+ memcpy(&gl->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke);+ offset += path->nstroke;+ }+ }++ // Quad+ call->triangleOffset = offset;+ call->triangleCount = 6;+ quad = &gl->verts[call->triangleOffset];+ glnvg__vset(&quad[0], bounds[0], bounds[3], 0.5f, 1.0f);+ glnvg__vset(&quad[1], bounds[2], bounds[3], 0.5f, 1.0f);+ glnvg__vset(&quad[2], bounds[2], bounds[1], 0.5f, 1.0f);++ glnvg__vset(&quad[3], bounds[0], bounds[3], 0.5f, 1.0f);+ glnvg__vset(&quad[4], bounds[2], bounds[1], 0.5f, 1.0f);+ glnvg__vset(&quad[5], bounds[0], bounds[1], 0.5f, 1.0f);++ // Setup uniforms for draw calls+ if (call->type == GLNVG_FILL) {+ call->uniformOffset = glnvg__allocFragUniforms(gl, 2);+ if (call->uniformOffset == -1) goto error;+ // Simple shader for stencil+ frag = nvg__fragUniformPtr(gl, call->uniformOffset);+ memset(frag, 0, sizeof(*frag));+ frag->strokeThr = -1.0f;+ frag->type = NSVG_SHADER_SIMPLE;+ // Fill shader+ glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, fringe, fringe, -1.0f);+ } else {+ call->uniformOffset = glnvg__allocFragUniforms(gl, 1);+ if (call->uniformOffset == -1) goto error;+ // Fill shader+ glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, fringe, fringe, -1.0f);+ }++ return;++error:+ // We get here if call alloc was ok, but something else is not.+ // Roll back the last call to prevent drawing it.+ if (gl->ncalls > 0) gl->ncalls--;+}++static void glnvg__renderStroke(void* uptr, NVGpaint* paint, NVGscissor* scissor, float fringe,+ float strokeWidth, const NVGpath* paths, int npaths)+{+ GLNVGcontext* gl = (GLNVGcontext*)uptr;+ GLNVGcall* call = glnvg__allocCall(gl);+ int i, maxverts, offset;++ if (call == NULL) return;++ call->type = GLNVG_STROKE;+ call->pathOffset = glnvg__allocPaths(gl, npaths);+ if (call->pathOffset == -1) goto error;+ call->pathCount = npaths;+ call->image = paint->image;++ // Allocate vertices for all the paths.+ maxverts = glnvg__maxVertCount(paths, npaths);+ offset = glnvg__allocVerts(gl, maxverts);+ if (offset == -1) goto error;++ for (i = 0; i < npaths; i++) {+ GLNVGpath* copy = &gl->paths[call->pathOffset + i];+ const NVGpath* path = &paths[i];+ memset(copy, 0, sizeof(GLNVGpath));+ if (path->nstroke) {+ copy->strokeOffset = offset;+ copy->strokeCount = path->nstroke;+ memcpy(&gl->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke);+ offset += path->nstroke;+ }+ }++ if (gl->flags & NVG_STENCIL_STROKES) {+ // Fill shader+ call->uniformOffset = glnvg__allocFragUniforms(gl, 2);+ if (call->uniformOffset == -1) goto error;++ glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f);+ glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f);++ } else {+ // Fill shader+ call->uniformOffset = glnvg__allocFragUniforms(gl, 1);+ if (call->uniformOffset == -1) goto error;+ glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f);+ }++ return;++error:+ // We get here if call alloc was ok, but something else is not.+ // Roll back the last call to prevent drawing it.+ if (gl->ncalls > 0) gl->ncalls--;+}++static void glnvg__renderTriangles(void* uptr, NVGpaint* paint, NVGscissor* scissor,+ const NVGvertex* verts, int nverts)+{+ GLNVGcontext* gl = (GLNVGcontext*)uptr;+ GLNVGcall* call = glnvg__allocCall(gl);+ GLNVGfragUniforms* frag;++ if (call == NULL) return;++ call->type = GLNVG_TRIANGLES;+ call->image = paint->image;++ // Allocate vertices for all the paths.+ call->triangleOffset = glnvg__allocVerts(gl, nverts);+ if (call->triangleOffset == -1) goto error;+ call->triangleCount = nverts;++ memcpy(&gl->verts[call->triangleOffset], verts, sizeof(NVGvertex) * nverts);++ // Fill shader+ call->uniformOffset = glnvg__allocFragUniforms(gl, 1);+ if (call->uniformOffset == -1) goto error;+ frag = nvg__fragUniformPtr(gl, call->uniformOffset);+ glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f, -1.0f);+ frag->type = NSVG_SHADER_IMG;++ return;++error:+ // We get here if call alloc was ok, but something else is not.+ // Roll back the last call to prevent drawing it.+ if (gl->ncalls > 0) gl->ncalls--;+}++static void glnvg__renderDelete(void* uptr)+{+ GLNVGcontext* gl = (GLNVGcontext*)uptr;+ int i;+ if (gl == NULL) return;++ glnvg__deleteShader(&gl->shader);++#if NANOVG_GL3+#if NANOVG_GL_USE_UNIFORMBUFFER+ if (gl->fragBuf != 0)+ glDeleteBuffers(1, &gl->fragBuf);+#endif+ if (gl->vertArr != 0)+ glDeleteVertexArrays(1, &gl->vertArr);+#endif+ if (gl->vertBuf != 0)+ glDeleteBuffers(1, &gl->vertBuf);++ for (i = 0; i < gl->ntextures; i++) {+ if (gl->textures[i].tex != 0 && (gl->textures[i].flags & NVG_IMAGE_NODELETE) == 0)+ glDeleteTextures(1, &gl->textures[i].tex);+ }+ free(gl->textures);++ free(gl->paths);+ free(gl->verts);+ free(gl->uniforms);+ free(gl->calls);++ free(gl);+}+++#if defined NANOVG_GL2+NVGcontext* nvgCreateGL2(int flags)+#elif defined NANOVG_GL3+NVGcontext* nvgCreateGL3(int flags)+#elif defined NANOVG_GLES2+NVGcontext* nvgCreateGLES2(int flags)+#elif defined NANOVG_GLES3+NVGcontext* nvgCreateGLES3(int flags)+#endif+{+ NVGparams params;+ NVGcontext* ctx = NULL;+ GLNVGcontext* gl = (GLNVGcontext*)malloc(sizeof(GLNVGcontext));+ if (gl == NULL) goto error;+ memset(gl, 0, sizeof(GLNVGcontext));++ memset(¶ms, 0, sizeof(params));+ params.renderCreate = glnvg__renderCreate;+ params.renderCreateTexture = glnvg__renderCreateTexture;+ params.renderDeleteTexture = glnvg__renderDeleteTexture;+ params.renderUpdateTexture = glnvg__renderUpdateTexture;+ params.renderGetTextureSize = glnvg__renderGetTextureSize;+ params.renderViewport = glnvg__renderViewport;+ params.renderCancel = glnvg__renderCancel;+ params.renderFlush = glnvg__renderFlush;+ params.renderFill = glnvg__renderFill;+ params.renderStroke = glnvg__renderStroke;+ params.renderTriangles = glnvg__renderTriangles;+ params.renderDelete = glnvg__renderDelete;+ params.userPtr = gl;+ params.edgeAntiAlias = flags & NVG_ANTIALIAS ? 1 : 0;++ gl->flags = flags;++ ctx = nvgCreateInternal(¶ms);+ if (ctx == NULL) goto error;++ return ctx;++error:+ // 'gl' is freed by nvgDeleteInternal.+ if (ctx != NULL) nvgDeleteInternal(ctx);+ return NULL;+}++#if defined NANOVG_GL2+void nvgDeleteGL2(NVGcontext* ctx)+#elif defined NANOVG_GL3+void nvgDeleteGL3(NVGcontext* ctx)+#elif defined NANOVG_GLES2+void nvgDeleteGLES2(NVGcontext* ctx)+#elif defined NANOVG_GLES3+void nvgDeleteGLES3(NVGcontext* ctx)+#endif+{+ nvgDeleteInternal(ctx);+}++#if defined NANOVG_GL2+int nvglCreateImageFromHandleGL2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)+#elif defined NANOVG_GL3+int nvglCreateImageFromHandleGL3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)+#elif defined NANOVG_GLES2+int nvglCreateImageFromHandleGLES2(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)+#elif defined NANOVG_GLES3+int nvglCreateImageFromHandleGLES3(NVGcontext* ctx, GLuint textureId, int w, int h, int imageFlags)+#endif+{+ GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr;+ GLNVGtexture* tex = glnvg__allocTexture(gl);++ if (tex == NULL) return 0;++ tex->type = NVG_TEXTURE_RGBA;+ tex->tex = textureId;+ tex->flags = imageFlags;+ tex->width = w;+ tex->height = h;++ return tex->id;+}++#if defined NANOVG_GL2+GLuint nvglImageHandleGL2(NVGcontext* ctx, int image)+#elif defined NANOVG_GL3+GLuint nvglImageHandleGL3(NVGcontext* ctx, int image)+#elif defined NANOVG_GLES2+GLuint nvglImageHandleGLES2(NVGcontext* ctx, int image)+#elif defined NANOVG_GLES3+GLuint nvglImageHandleGLES3(NVGcontext* ctx, int image)+#endif+{+ GLNVGcontext* gl = (GLNVGcontext*)nvgInternalParams(ctx)->userPtr;+ GLNVGtexture* tex = glnvg__findTexture(gl, image);+ return tex->tex;+}++#endif /* NANOVG_GL_IMPLEMENTATION */
+ nanovg/src/stb_image.h view
@@ -0,0 +1,6614 @@+/* stb_image - v2.10 - public domain image loader - http://nothings.org/stb_image.h+ 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-bit-per-channel (16 bpc not supported)++ 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.+++ Revision 2.00 release notes:++ - Progressive JPEG is now supported.++ - PPM and PGM binary formats are now supported, thanks to Ken Miller.++ - x86 platforms now make use of SSE2 SIMD instructions for+ JPEG decoding, and ARM platforms can use NEON SIMD if requested.+ This work was done by Fabian "ryg" Giesen. SSE2 is used by+ default, but NEON must be enabled explicitly; see docs.++ With other JPEG optimizations included in this version, we see+ 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup+ on a JPEG on an ARM machine, relative to previous versions of this+ library. The same results will not obtain for all JPGs and for all+ x86/ARM machines. (Note that progressive JPEGs are significantly+ slower to decode than regular JPEGs.) This doesn't mean that this+ is the fastest JPEG decoder in the land; rather, it brings it+ closer to parity with standard libraries. If you want the fastest+ decode, look elsewhere. (See "Philosophy" section of docs below.)++ See final bullet items below for more info on SIMD.++ - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing+ the memory allocator. Unlike other STBI libraries, these macros don't+ support a context parameter, so if you need to pass a context in to+ the allocator, you'll have to store it in a global or a thread-local+ variable.++ - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and+ STBI_NO_LINEAR.+ STBI_NO_HDR: suppress implementation of .hdr reader format+ STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API++ - 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)++ Note that you can define multiples of these, and you will get all+ of them ("only x" and "only y" is interpreted to mean "only x&y").++ - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still+ want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB++ - Compilation of all SIMD code can be suppressed with+ #define STBI_NO_SIMD+ It should not be necessary to disable SIMD unless you have issues+ compiling (e.g. using an x86 compiler which doesn't support SSE+ intrinsics or that doesn't support the method used to detect+ SSE2 support at run-time), and even those can be reported as+ bugs so I can refine the built-in compile-time checking to be+ smarter.++ - The old STBI_SIMD system which allowed installing a user-defined+ IDCT etc. has been removed. If you need this, don't upgrade. My+ assumption is that almost nobody was doing this, and those who+ were will find the built-in SIMD more satisfactory anyway.++ - RGB values computed for JPEG images are slightly different from+ previous versions of stb_image. (This is due to using less+ integer precision in SIMD.) The C code has been adjusted so+ that the same RGB values will be computed regardless of whether+ SIMD support is available, so your app should always produce+ consistent results. But these results are slightly different from+ previous versions. (Specifically, about 3% of available YCbCr values+ will compute different RGB results from pre-1.49 versions by +-1;+ most of the deviating values are one smaller in the G channel.)++ - If you must produce consistent results with previous versions of+ stb_image, #define STBI_JPEG_OLD and you will get the same results+ you used to; however, you will not get the SIMD speedups for+ the YCbCr-to-RGB conversion step (although you should still see+ significant JPEG speedup from the other changes).++ Please note that STBI_JPEG_OLD is a temporary feature; it will be+ removed in future versions of the library. It is only intended for+ near-term back-compatibility use.+++ Latest revision history:+ 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+ 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA+ 2.07 (2015-09-13) partial animated GIF support+ limited 16-bit PSD support+ minor bugs, code cleanup, and compiler warnings+ 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) additional corruption checking+ stbi_set_flip_vertically_on_load+ fix NEON support; fix mingw support+ 2.02 (2015-01-19) fix incorrect assert, fix warning+ 2.01 (2015-01-17) fix various warnings+ 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG+ 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD+ progressive JPEG+ PGM/PPM support+ STBI_MALLOC,STBI_REALLOC,STBI_FREE+ STBI_NO_*, STBI_ONLY_*+ GIF bugfix+ 1.48 (2014-12-14) fix incorrectly-named assert()+ 1.47 (2014-12-14) 1/2/4-bit PNG support (both grayscale and paletted)+ optimize PNG+ fix bug in interlaced PNG with user-specified channel count++ 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)+ urraka@github (animated gif) Junggon Kim (PNM comments)+ Daniel Gibson (16-bit TGA)++ Optimizations & bugfixes+ Fabian "ryg" Giesen+ Arseny Kapoulkine++ Bug & warning fixes+ Marc LeBlanc David Woo Guillaume George Martins Mozeiko+ Christpher Lloyd Martin Golini Jerry Jansson Joseph Thomson+ Dave Moore Roy Eltham Hayaki Saito Phil Jordan+ Won Chun Luke Graham Johan Duparc Nathan Reed+ the Horde3D community Thomas Ruf Ronny Chevalier Nick Verigakis+ Janez Zemva John Bartholomew Michal Cichon svdijk@github+ Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson+ Laurent Gomila Cort Stratton Sergio Gonzalez romigrou@github+ Aruelien Pocheville Thibault Reuille Cass Everitt+ Ryamond Barbiero Paul Du Bois Engin Manap+ Blazej Dariusz Roszkowski+ Michaelangel007@github+++LICENSE++This software is in the public domain. Where that dedication is not+recognized, you are granted a perpetual, irrevocable license to copy,+distribute, and modify this file as you see fit.++*/++#ifndef STBI_INCLUDE_STB_IMAGE_H+#define STBI_INCLUDE_STB_IMAGE_H++// DOCUMENTATION+//+// Limitations:+// - no 16-bit-per-channel PNG+// - no 12-bit-per-channel JPEG+// - no JPEGs with arithmetic coding+// - no 1-bit BMP+// - 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 *comp -- outputs # of image components in image file+// int req_comp -- 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 'req_comp' if req_comp is non-zero, or *comp otherwise.+// If req_comp is non-zero, *comp has the number of components that _would_+// have been output otherwise. E.g. if you set req_comp to 4, you will always+// get RGBA output, but you can check *comp 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, *comp 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.+//+// ===========================================================================+//+// 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+// make more explicit reasons why performance can't be emphasized.+//+// - Portable ("ease of use")+// - Small 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.+//+// The output of the JPEG decoder is slightly different from versions where+// SIMD support was introduced (that is, for versions before 1.49). The+// difference is only +-1 in the 8-bit RGB channels, and only on a small+// fraction of pixels. You can force the pre-1.49 behavior by defining+// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path+// and hence cost some performance.+//+// 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 now supports loading HDR images in general, and currently+// the Radiance .HDR file format, although the support is provided+// generically. 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:+//+// By default we convert iphone-formatted PNGs back to RGB, even though+// they are internally encoded differently. You can disable this conversion+// by by calling stbi_convert_iphone_png_to_rgb(0), in which case+// you will always just get the native iphone "format" through (which+// is BGR stored in RGB).+//+// 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).+//+++#ifndef STBI_NO_STDIO+#include <stdio.h>+#endif // STBI_NO_STDIO++#define STBI_VERSION 1++enum+{+ STBI_default = 0, // only used for req_comp++ STBI_grey = 1,+ STBI_grey_alpha = 2,+ STBI_rgb = 3,+ STBI_rgb_alpha = 4+};++typedef unsigned char stbi_uc;++#ifdef __cplusplus+extern "C" {+#endif++#ifdef STB_IMAGE_STATIC+#define STBIDEF static+#else+#define STBIDEF extern+#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;++STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp);+STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int 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);++#ifndef STBI_NO_STDIO+STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);+// for stbi_load_from_file, file pointer is left pointing immediately after image+#endif++#ifndef STBI_NO_LINEAR+ STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp);+ STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp);+ STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp);++ #ifndef STBI_NO_STDIO+ STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp);+ #endif+#endif++#ifndef STBI_NO_HDR+ STBIDEF void stbi_hdr_to_ldr_gamma(float gamma);+ STBIDEF void stbi_hdr_to_ldr_scale(float scale);+#endif // STBI_NO_HDR++#ifndef STBI_NO_LINEAR+ STBIDEF void stbi_ldr_to_hdr_gamma(float gamma);+ STBIDEF void stbi_ldr_to_hdr_scale(float scale);+#endif // STBI_NO_LINEAR++// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR+STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);+STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);+#ifndef STBI_NO_STDIO+STBIDEF int stbi_is_hdr (char const *filename);+STBIDEF int stbi_is_hdr_from_file(FILE *f);+#endif // STBI_NO_STDIO+++// get a VERY brief reason for failure+// NOT 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);++#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);++#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);++// ZLIB client - used by PNG, available for other purposes++STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen);+STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header);+STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen);+STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);++STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen);+STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen);+++#ifdef __cplusplus+}+#endif++//+//+//// end header file /////////////////////////////////////////////////////+#endif // STBI_INCLUDE_STB_IMAGE_H++#ifdef STB_IMAGE_IMPLEMENTATION++#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \+ || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \+ || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \+ || defined(STBI_ONLY_ZLIB)+ #ifndef STBI_ONLY_JPEG+ #define STBI_NO_JPEG+ #endif+ #ifndef STBI_ONLY_PNG+ #define STBI_NO_PNG+ #endif+ #ifndef STBI_ONLY_BMP+ #define STBI_NO_BMP+ #endif+ #ifndef STBI_ONLY_PSD+ #define STBI_NO_PSD+ #endif+ #ifndef STBI_ONLY_TGA+ #define STBI_NO_TGA+ #endif+ #ifndef STBI_ONLY_GIF+ #define STBI_NO_GIF+ #endif+ #ifndef STBI_ONLY_HDR+ #define STBI_NO_HDR+ #endif+ #ifndef STBI_ONLY_PIC+ #define STBI_NO_PIC+ #endif+ #ifndef STBI_ONLY_PNM+ #define STBI_NO_PNM+ #endif+#endif++#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB)+#define STBI_NO_ZLIB+#endif+++#include <stdarg.h>+#include <stddef.h> // ptrdiff_t on osx+#include <stdlib.h>+#include <string.h>++#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR)+#include <math.h> // ldexp+#endif++#ifndef STBI_NO_STDIO+#include <stdio.h>+#endif++#ifndef STBI_ASSERT+#include <assert.h>+#define STBI_ASSERT(x) assert(x)+#endif+++#ifndef _MSC_VER+ #ifdef __cplusplus+ #define stbi_inline inline+ #else+ #define stbi_inline+ #endif+#else+ #define stbi_inline __forceinline+#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) >> (32 - (y))))+#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(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD)+// NOTE: not clear do we actually need this for the 64-bit path?+// gcc doesn't support sse2 intrinsics unless you compile with -msse2,+// (but compiling with -msse2 allows the compiler to use SSE2 everywhere;+// this is just broken and gcc are jerks for not fixing it properly+// http://www.virtualdub.org/blog/pivot/entry.php?id=363 )+#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)+#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++static int stbi__sse2_available()+{+ int info3 = stbi__cpuid3();+ return ((info3 >> 26) & 1) != 0;+}+#else // assume GCC-style if not VC+++#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))++static int stbi__sse2_available()+{+#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later+ // GCC 4.8+ has a nice way to do this+ return __builtin_cpu_supports("sse2");+#else+ // portable way to do this, preferably without using GCC inline ASM?+ // just bail for now.+ return 0;+#endif+}+#endif+#endif++// ARM NEON+#if defined(STBI_NO_SIMD) && defined(STBI_NEON)+#undef STBI_NEON+#endif++#ifdef STBI_NEON+#include <arm_neon.h>+// assume GCC or Clang on ARM targets+#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16)))+#endif++#ifndef STBI_SIMD_ALIGN+#define STBI_SIMD_ALIGN(type, name) type name+#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];++ 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->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->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)+{+ fseek((FILE*) user, n, SEEK_CUR);+}++static int stbi__stdio_eof(void *user)+{+ return feof((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;+}++#ifndef STBI_NO_JPEG+static int stbi__jpeg_test(stbi__context *s);+static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);+static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp);+#endif++#ifndef STBI_NO_PNG+static int stbi__png_test(stbi__context *s);+static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);+static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp);+#endif++#ifndef STBI_NO_BMP+static int stbi__bmp_test(stbi__context *s);+static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);+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 stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);+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 stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);+static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp);+#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);+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 stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);+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 stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, 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 stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp);+static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp);+#endif++// this is not threadsafe+static 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;+}++static void *stbi__malloc(size_t size)+{+ return STBI_MALLOC(size);+}++// 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 = 0;++STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip)+{+ stbi__vertically_flip_on_load = flag_true_if_should_flip;+}++static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp)+{+ #ifndef STBI_NO_JPEG+ if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp);+ #endif+ #ifndef STBI_NO_PNG+ if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp);+ #endif+ #ifndef STBI_NO_BMP+ if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp);+ #endif+ #ifndef STBI_NO_GIF+ if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp);+ #endif+ #ifndef STBI_NO_PSD+ if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp);+ #endif+ #ifndef STBI_NO_PIC+ if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp);+ #endif+ #ifndef STBI_NO_PNM+ if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp);+ #endif++ #ifndef STBI_NO_HDR+ if (stbi__hdr_test(s)) {+ float *hdr = stbi__hdr_load(s, x,y,comp,req_comp);+ 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);+ #endif++ return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt");+}++static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp)+{+ unsigned char *result = stbi__load_main(s, x, y, comp, req_comp);++ if (stbi__vertically_flip_on_load && result != NULL) {+ int w = *x, h = *y;+ int depth = req_comp ? req_comp : *comp;+ int row,col,z;+ stbi_uc temp;++ // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once+ for (row = 0; row < (h>>1); row++) {+ for (col = 0; col < w; col++) {+ for (z = 0; z < depth; z++) {+ temp = result[(row * w + col) * depth + z];+ result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z];+ result[((h - row - 1) * w + col) * depth + z] = temp;+ }+ }+ }+ }++ return result;+}++#ifndef STBI_NO_HDR+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 w = *x, h = *y;+ int depth = req_comp ? req_comp : *comp;+ int row,col,z;+ float temp;++ // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once+ for (row = 0; row < (h>>1); row++) {+ for (col = 0; col < w; col++) {+ for (z = 0; z < depth; z++) {+ temp = result[(row * w + col) * depth + z];+ result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z];+ result[((h - row - 1) * w + col) * depth + z] = temp;+ }+ }+ }+ }+}+#endif++#ifndef STBI_NO_STDIO++static FILE *stbi__fopen(char const *filename, char const *mode)+{+ FILE *f;+#if 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_flip(&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;+}+#endif //!STBI_NO_STDIO++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_flip(&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_flip(&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;+ #ifndef STBI_NO_HDR+ if (stbi__hdr_test(s)) {+ float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp);+ if (hdr_data)+ stbi__float_postprocess(hdr_data,x,y,comp,req_comp);+ return hdr_data;+ }+ #endif+ data = stbi__load_flip(s, x, y, comp, req_comp);+ if (data)+ return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp);+ return stbi__errpf("unknown image type", "Image not of any known type, or corrupt");+}++STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp)+{+ stbi__context s;+ stbi__start_mem(&s,buffer,len);+ return stbi__loadf_main(&s,x,y,comp,req_comp);+}++STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp)+{+ stbi__context s;+ stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);+ return stbi__loadf_main(&s,x,y,comp,req_comp);+}++#ifndef STBI_NO_STDIO+STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp)+{+ float *result;+ FILE *f = stbi__fopen(filename, "rb");+ if (!f) return stbi__errpf("can't fopen", "Unable to open file");+ result = stbi_loadf_from_file(f,x,y,comp,req_comp);+ fclose(f);+ return result;+}++STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp)+{+ stbi__context s;+ stbi__start_file(&s,f);+ return stbi__loadf_main(&s,x,y,comp,req_comp);+}+#endif // !STBI_NO_STDIO++#endif // !STBI_NO_LINEAR++// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is+// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always+// reports false!++STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len)+{+ #ifndef STBI_NO_HDR+ stbi__context s;+ stbi__start_mem(&s,buffer,len);+ return stbi__hdr_test(&s);+ #else+ STBI_NOTUSED(buffer);+ STBI_NOTUSED(len);+ return 0;+ #endif+}++#ifndef STBI_NO_STDIO+STBIDEF int stbi_is_hdr (char const *filename)+{+ FILE *f = stbi__fopen(filename, "rb");+ int result=0;+ if (f) {+ result = stbi_is_hdr_from_file(f);+ fclose(f);+ }+ return result;+}++STBIDEF int stbi_is_hdr_from_file(FILE *f)+{+ #ifndef STBI_NO_HDR+ stbi__context s;+ stbi__start_file(&s,f);+ return stbi__hdr_test(&s);+ #else+ STBI_NOTUSED(f);+ return 0;+ #endif+}+#endif // !STBI_NO_STDIO++STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user)+{+ #ifndef STBI_NO_HDR+ stbi__context s;+ stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user);+ return stbi__hdr_test(&s);+ #else+ STBI_NOTUSED(clbk);+ STBI_NOTUSED(user);+ return 0;+ #endif+}++#ifndef STBI_NO_LINEAR+static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f;++STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; }+STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; }+#endif++static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f;++STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; }+STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; }+++//////////////////////////////////////////////////////////////////////////////+//+// Common code used by all image loaders+//++enum+{+ STBI__SCAN_load=0,+ STBI__SCAN_type,+ STBI__SCAN_header+};++static void stbi__refill_buffer(stbi__context *s)+{+ int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen);+ if (n == 0) {+ // at end of file, treat same as if from memory, but need to handle case+ // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file+ s->read_from_callbacks = 0;+ s->img_buffer = s->buffer_start;+ s->img_buffer_end = s->buffer_start+1;+ *s->img_buffer = 0;+ } else {+ s->img_buffer = s->buffer_start;+ s->img_buffer_end = s->buffer_start + n;+ }+}++stbi_inline static stbi_uc stbi__get8(stbi__context *s)+{+ if (s->img_buffer < s->img_buffer_end)+ return *s->img_buffer++;+ if (s->read_from_callbacks) {+ stbi__refill_buffer(s);+ return *s->img_buffer++;+ }+ return 0;+}++stbi_inline static 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;+}++static void stbi__skip(stbi__context *s, int n)+{+ 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);+}++#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);+ return z + (stbi__get16le(s) << 16);+}+#endif++#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(req_comp * x * y);+ 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 COMBO(a,b) ((a)*8+(b))+ #define CASE(a,b) case 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 (COMBO(img_n, req_comp)) {+ CASE(1,2) dest[0]=src[0], dest[1]=255; break;+ CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break;+ CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break;+ CASE(2,1) dest[0]=src[0]; break;+ CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break;+ CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break;+ CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break;+ CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break;+ CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break;+ CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break;+ CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break;+ CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break;+ default: STBI_ASSERT(0);+ }+ #undef 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 = (float *) stbi__malloc(x * y * comp * sizeof(float));+ 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 (k < comp) output[i*comp + k] = data[i*comp+k]/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 = (stbi_uc *) stbi__malloc(x * y * comp);+ 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_uc 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 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,code;+ // build size list for each symbol (from JPEG spec)+ for (i=0; i < 16; ++i)+ for (j=0; j < count[i]; ++j)+ h->size[k++] = (stbi_uc) (i+1);+ h->size[k] = 0;++ // compute actual symbols (from jpeg spec)+ code = 0;+ k = 0;+ for(j=1; j <= 16; ++j) {+ // compute delta to add to code to compute symbol id+ h->delta[j] = k - code;+ if (h->size[k] == j) {+ while (h->size[k] == j)+ h->code[k++] = (stbi__uint16) (code++);+ if (code-1 >= (1 << 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 += (-1 << 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 << 8) + (run << 4) + (len + magbits));+ }+ }+ }+}++static void stbi__grow_buffer_unsafe(stbi__jpeg *j)+{+ do {+ int b = j->nomore ? 0 : stbi__get8(j->s);+ if (b == 0xff) {+ int c = stbi__get8(j->s);+ 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 stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535};++// decode a jpeg huffman value from the bitstream+stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h)+{+ unsigned int temp;+ int c,k;++ if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);++ // look at the top FAST_BITS and determine what symbol ID it is,+ // if the code is <= FAST_BITS+ c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);+ k = h->fast[c];+ if (k < 255) {+ int s = h->size[k];+ if (s > j->code_bits)+ return -1;+ j->code_buffer <<= s;+ j->code_bits -= s;+ return h->values[k];+ }++ // naive test is to shift the code_buffer down so k bits are+ // valid, then test against maxcode. To speed this up, we've+ // preshifted maxcode left so that it has (16-k) 0s at the+ // end; in other words, regardless of the number of bits, it+ // wants to be compared against something shifted to have 16;+ // that way we don't need to shift inside the loop.+ temp = j->code_buffer >> 16;+ for (k=FAST_BITS+1 ; ; ++k)+ if (temp < h->maxcode[k])+ break;+ if (k == 17) {+ // error! code not found+ j->code_bits -= 16;+ return -1;+ }++ if (k > j->code_bits)+ return -1;++ // convert the huffman code to the symbol id+ c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k];+ STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]);++ // convert the id to a symbol+ j->code_bits -= k;+ j->code_buffer <<= k;+ return h->values[c];+}++// bias[n] = (-1<<n) + 1+static int const stbi__jbias[16] = {0,-1,-3,-7,-15,-31,-63,-127,-255,-511,-1023,-2047,-4095,-8191,-16383,-32767};++// combined JPEG 'receive' and JPEG 'extend', since baseline+// always extends everything it receives.+stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n)+{+ unsigned int k;+ int sgn;+ if (j->code_bits < n) stbi__grow_buffer_unsafe(j);++ sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB+ k = stbi_lrot(j->code_buffer, n);+ STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask)));+ j->code_buffer = k & ~stbi__bmask[n];+ k &= stbi__bmask[n];+ j->code_bits -= n;+ return k + (stbi__jbias[n] & ~sgn);+}++// get some unsigned bits+stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n)+{+ unsigned int k;+ if (j->code_bits < n) stbi__grow_buffer_unsafe(j);+ k = stbi_lrot(j->code_buffer, n);+ j->code_buffer = k & ~stbi__bmask[n];+ k &= stbi__bmask[n];+ j->code_bits -= n;+ return k;+}++stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j)+{+ unsigned int k;+ if (j->code_bits < 1) stbi__grow_buffer_unsafe(j);+ k = j->code_buffer;+ j->code_buffer <<= 1;+ --j->code_bits;+ return k & 0x80000000;+}++// given a value that's at position X in the zigzag stream,+// where does it appear in the 8x8 matrix coded as row-major?+static 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_uc *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) return stbi__err("bad huffman code","Corrupt JPEG");++ // 0 all the ac values now so we can do it 32-bits at a time+ memset(data,0,64*sizeof(data[0]));++ diff = t ? stbi__extend_receive(j, t) : 0;+ dc = j->img_comp[b].dc_pred + diff;+ j->img_comp[b].dc_pred = dc;+ data[0] = (short) (dc * dequant[0]);++ // decode AC components, see JPEG spec+ k = 1;+ do {+ unsigned int zig;+ int c,r,s;+ if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);+ c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);+ r = fac[c];+ if (r) { // fast-AC path+ k += (r >> 4) & 15; // run+ s = r & 15; // combined length+ j->code_buffer <<= s;+ j->code_bits -= s;+ // decode into unzigzag'd location+ zig = stbi__jpeg_dezigzag[k++];+ data[zig] = (short) ((r >> 8) * dequant[zig]);+ } else {+ int rs = stbi__jpeg_huff_decode(j, hac);+ if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG");+ s = rs & 15;+ r = rs >> 4;+ if (s == 0) {+ if (rs != 0xf0) break; // end block+ k += 16;+ } else {+ k += r;+ // decode into unzigzag'd location+ zig = stbi__jpeg_dezigzag[k++];+ data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]);+ }+ }+ } while (k < 64);+ return 1;+}++static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b)+{+ int diff,dc;+ int t;+ if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");++ if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);++ if (j->succ_high == 0) {+ // first scan for DC coefficient, must be first+ memset(data,0,64*sizeof(data[0])); // 0 all the ac values now+ t = stbi__jpeg_huff_decode(j, hdc);+ diff = t ? stbi__extend_receive(j, t) : 0;++ dc = j->img_comp[b].dc_pred + diff;+ j->img_comp[b].dc_pred = dc;+ data[0] = (short) (dc << j->succ_low);+ } else {+ // refinement scan for DC coefficient+ if (stbi__jpeg_get_bit(j))+ data[0] += (short) (1 << j->succ_low);+ }+ return 1;+}++// @OPTIMIZE: store non-zigzagged during the decode passes,+// and only de-zigzag when dequantizing+static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac)+{+ int k;+ if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG");++ if (j->succ_high == 0) {+ int shift = j->succ_low;++ if (j->eob_run) {+ --j->eob_run;+ return 1;+ }++ k = j->spec_start;+ do {+ unsigned int zig;+ int c,r,s;+ if (j->code_bits < 16) stbi__grow_buffer_unsafe(j);+ c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1);+ r = fac[c];+ if (r) { // fast-AC path+ k += (r >> 4) & 15; // run+ s = r & 15; // combined length+ j->code_buffer <<= s;+ j->code_bits -= s;+ zig = stbi__jpeg_dezigzag[k++];+ data[zig] = (short) ((r >> 8) << 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) << 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) << 12)++// 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] << 2;+ 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);+ 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 = 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_uc *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;+ int t = q & 15,i;+ if (p != 0) 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__get8(z->s);+ L -= 65;+ }+ return L==0;++ case 0xC4: // DHT - define huffman table+ L = stbi__get16be(z->s)-2;+ while (L > 0) {+ stbi_uc *v;+ int sizes[16],i,n=0;+ int q = stbi__get8(z->s);+ int tc = q >> 4;+ int th = q & 15;+ if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG");+ for (i=0; i < 16; ++i) {+ sizes[i] = stbi__get8(z->s);+ n += sizes[i];+ }+ L -= 17;+ if (tc == 0) {+ if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0;+ v = z->huff_dc[th].values;+ } else {+ if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0;+ v = z->huff_ac[th].values;+ }+ for (i=0; i < n; ++i)+ v[i] = stbi__get8(z->s);+ if (tc != 0)+ stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th);+ L -= n;+ }+ return L==0;+ }+ // check for comment block or APP blocks+ if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) {+ stbi__skip(z->s, stbi__get16be(z->s)-2);+ return 1;+ }+ return 0;+}++// 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__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+ c = stbi__get8(s);+ if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires+ 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");++ for (i=0; i < s->img_n; ++i) {+ z->img_comp[i].id = stbi__get8(s);+ if (z->img_comp[i].id != i+1) // JFIF requires+ if (z->img_comp[i].id != i) // some version of jpegtran outputs non-JFIF-compliant files!+ return stbi__err("bad component ID","Corrupt JPEG");+ 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 ((1 << 30) / s->img_x / s->img_n < s->img_y) 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;+ }++ // 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;+ 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+ 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].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15);++ if (z->img_comp[i].raw_data == NULL) {+ for(--i; i >= 0; --i) {+ STBI_FREE(z->img_comp[i].raw_data);+ z->img_comp[i].raw_data = NULL;+ }+ return 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);+ z->img_comp[i].linebuf = NULL;+ if (z->progressive) {+ z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3;+ z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3;+ z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15);+ z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15);+ } else {+ z->img_comp[i].coeff = 0;+ z->img_comp[i].raw_coeff = 0;+ }+ }++ 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->marker = STBI__MARKER_none; // initialize cached marker to empty+ m = stbi__get_marker(z);+ if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG");+ if (scan == STBI__SCAN_type) return 1;+ m = stbi__get_marker(z);+ while (!stbi__SOF(m)) {+ if (!stbi__process_marker(z,m)) return 0;+ m = stbi__get_marker(z);+ while (m == STBI__MARKER_none) {+ // some files have extra padding after their blocks, so ok, we'll scan+ if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG");+ m = stbi__get_marker(z);+ }+ }+ z->progressive = stbi__SOF_progressive(m);+ if (!stbi__process_frame_header(z, scan)) return 0;+ return 1;+}++// decode image to YCbCr format+static int stbi__decode_jpeg_image(stbi__jpeg *j)+{+ int m;+ for (m = 0; m < 4; m++) {+ j->img_comp[m].raw_data = NULL;+ j->img_comp[m].raw_coeff = NULL;+ }+ j->restart_interval = 0;+ if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0;+ m = stbi__get_marker(j);+ while (!stbi__EOI(m)) {+ if (stbi__SOS(m)) {+ if (!stbi__process_scan_header(j)) return 0;+ if (!stbi__parse_entropy_coded_data(j)) return 0;+ if (j->marker == STBI__MARKER_none ) {+ // handle 0s at the end of image data from IP Kamera 9060+ while (!stbi__at_eof(j->s)) {+ int x = stbi__get8(j->s);+ if (x == 255) {+ j->marker = stbi__get8(j->s);+ break;+ } else if (x != 0) {+ return stbi__err("junk before marker", "Corrupt JPEG");+ }+ }+ // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0+ }+ } else {+ if (!stbi__process_marker(j, m)) return 0;+ }+ m = stbi__get_marker(j);+ }+ if (j->progressive)+ stbi__jpeg_finish(j);+ return 1;+}++// static jfif-centered resampling (across block boundaries)++typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1,+ int w, int hs);++#define stbi__div4(x) ((stbi_uc) ((x) >> 2))++static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)+{+ STBI_NOTUSED(out);+ STBI_NOTUSED(in_far);+ STBI_NOTUSED(w);+ STBI_NOTUSED(hs);+ return in_near;+}++static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)+{+ // need to generate two samples vertically for every one in input+ int i;+ STBI_NOTUSED(hs);+ for (i=0; i < w; ++i)+ out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2);+ return out;+}++static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)+{+ // need to generate two samples horizontally for every one in input+ int i;+ stbi_uc *input = in_near;++ if (w == 1) {+ // if only one sample, can't do any interpolation+ out[0] = out[1] = input[0];+ return out;+ }++ out[0] = input[0];+ out[1] = stbi__div4(input[0]*3 + input[1] + 2);+ for (i=1; i < w-1; ++i) {+ int n = 3*input[i]+2;+ out[i*2+0] = stbi__div4(n+input[i-1]);+ out[i*2+1] = stbi__div4(n+input[i+1]);+ }+ out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2);+ out[i*2+1] = input[w-1];++ STBI_NOTUSED(in_far);+ STBI_NOTUSED(hs);++ return out;+}++#define stbi__div16(x) ((stbi_uc) ((x) >> 4))++static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)+{+ // need to generate 2x2 samples for every one in input+ int i,t0,t1;+ if (w == 1) {+ out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);+ return out;+ }++ t1 = 3*in_near[0] + in_far[0];+ out[0] = stbi__div4(t1+2);+ for (i=1; i < w; ++i) {+ t0 = t1;+ t1 = 3*in_near[i]+in_far[i];+ out[i*2-1] = stbi__div16(3*t0 + t1 + 8);+ out[i*2 ] = stbi__div16(3*t1 + t0 + 8);+ }+ out[w*2-1] = stbi__div4(t1+2);++ STBI_NOTUSED(hs);++ return out;+}++#if defined(STBI_SSE2) || defined(STBI_NEON)+static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)+{+ // need to generate 2x2 samples for every one in input+ int i=0,t0,t1;++ if (w == 1) {+ out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2);+ return out;+ }++ t1 = 3*in_near[0] + in_far[0];+ // process groups of 8 pixels for as long as we can.+ // note we can't handle the last pixel in a row in this loop+ // because we need to handle the filter boundary conditions.+ for (; i < ((w-1) & ~7); i += 8) {+#if defined(STBI_SSE2)+ // load and perform the vertical filtering pass+ // this uses 3*x + y = 4*x + (y - x)+ __m128i zero = _mm_setzero_si128();+ __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i));+ __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i));+ __m128i farw = _mm_unpacklo_epi8(farb, zero);+ __m128i nearw = _mm_unpacklo_epi8(nearb, zero);+ __m128i diff = _mm_sub_epi16(farw, nearw);+ __m128i nears = _mm_slli_epi16(nearw, 2);+ __m128i curr = _mm_add_epi16(nears, diff); // current row++ // horizontal filter works the same based on shifted vers of current+ // row. "prev" is current row shifted right by 1 pixel; we need to+ // insert the previous pixel value (from t1).+ // "next" is current row shifted left by 1 pixel, with first pixel+ // of next block of 8 pixels added in.+ __m128i prv0 = _mm_slli_si128(curr, 2);+ __m128i nxt0 = _mm_srli_si128(curr, 2);+ __m128i prev = _mm_insert_epi16(prv0, t1, 0);+ __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7);++ // horizontal filter, polyphase implementation since it's convenient:+ // even pixels = 3*cur + prev = cur*4 + (prev - cur)+ // odd pixels = 3*cur + next = cur*4 + (next - cur)+ // note the shared term.+ __m128i bias = _mm_set1_epi16(8);+ __m128i curs = _mm_slli_epi16(curr, 2);+ __m128i prvd = _mm_sub_epi16(prev, curr);+ __m128i nxtd = _mm_sub_epi16(next, curr);+ __m128i curb = _mm_add_epi16(curs, bias);+ __m128i even = _mm_add_epi16(prvd, curb);+ __m128i odd = _mm_add_epi16(nxtd, curb);++ // interleave even and odd pixels, then undo scaling.+ __m128i int0 = _mm_unpacklo_epi16(even, odd);+ __m128i int1 = _mm_unpackhi_epi16(even, odd);+ __m128i de0 = _mm_srli_epi16(int0, 4);+ __m128i de1 = _mm_srli_epi16(int1, 4);++ // pack and write output+ __m128i outv = _mm_packus_epi16(de0, de1);+ _mm_storeu_si128((__m128i *) (out + i*2), outv);+#elif defined(STBI_NEON)+ // load and perform the vertical filtering pass+ // this uses 3*x + y = 4*x + (y - x)+ uint8x8_t farb = vld1_u8(in_far + i);+ uint8x8_t nearb = vld1_u8(in_near + i);+ int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb));+ int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2));+ int16x8_t curr = vaddq_s16(nears, diff); // current row++ // horizontal filter works the same based on shifted vers of current+ // row. "prev" is current row shifted right by 1 pixel; we need to+ // insert the previous pixel value (from t1).+ // "next" is current row shifted left by 1 pixel, with first pixel+ // of next block of 8 pixels added in.+ int16x8_t prv0 = vextq_s16(curr, curr, 7);+ int16x8_t nxt0 = vextq_s16(curr, curr, 1);+ int16x8_t prev = vsetq_lane_s16(t1, prv0, 0);+ int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7);++ // horizontal filter, polyphase implementation since it's convenient:+ // even pixels = 3*cur + prev = cur*4 + (prev - cur)+ // odd pixels = 3*cur + next = cur*4 + (next - cur)+ // note the shared term.+ int16x8_t curs = vshlq_n_s16(curr, 2);+ int16x8_t prvd = vsubq_s16(prev, curr);+ int16x8_t nxtd = vsubq_s16(next, curr);+ int16x8_t even = vaddq_s16(curs, prvd);+ int16x8_t odd = vaddq_s16(curs, nxtd);++ // undo scaling and round, then store with even/odd phases interleaved+ uint8x8x2_t o;+ o.val[0] = vqrshrun_n_s16(even, 4);+ o.val[1] = vqrshrun_n_s16(odd, 4);+ vst2_u8(out + i*2, o);+#endif++ // "previous" value for next iter+ t1 = 3*in_near[i+7] + in_far[i+7];+ }++ t0 = t1;+ t1 = 3*in_near[i] + in_far[i];+ out[i*2] = stbi__div16(3*t1 + t0 + 8);++ for (++i; i < w; ++i) {+ t0 = t1;+ t1 = 3*in_near[i]+in_far[i];+ out[i*2-1] = stbi__div16(3*t0 + t1 + 8);+ out[i*2 ] = stbi__div16(3*t1 + t0 + 8);+ }+ out[w*2-1] = stbi__div4(t1+2);++ STBI_NOTUSED(hs);++ return out;+}+#endif++static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs)+{+ // resample with nearest-neighbor+ int i,j;+ STBI_NOTUSED(in_far);+ for (i=0; i < w; ++i)+ for (j=0; j < hs; ++j)+ out[i*hs+j] = in_near[i];+ return out;+}++#ifdef STBI_JPEG_OLD+// this is the same YCbCr-to-RGB calculation that stb_image has used+// historically before the algorithm changes in 1.49+#define float2fixed(x) ((int) ((x) * 65536 + 0.5))+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] << 16) + 32768; // rounding+ int r,g,b;+ int cr = pcr[i] - 128;+ int cb = pcb[i] - 128;+ r = y_fixed + cr*float2fixed(1.40200f);+ g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f);+ b = y_fixed + cb*float2fixed(1.77200f);+ r >>= 16;+ g >>= 16;+ b >>= 16;+ 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;+ }+}+#else+// 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 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* float2fixed(1.40200f);+ g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000);+ b = y_fixed + cb* 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++#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* float2fixed(1.40200f);+ g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000);+ b = y_fixed + cb* 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;+ #ifndef STBI_JPEG_OLD+ j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;+ #endif+ j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd;+ }+#endif++#ifdef STBI_NEON+ j->idct_block_kernel = stbi__idct_simd;+ #ifndef STBI_JPEG_OLD+ j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd;+ #endif+ 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)+{+ int i;+ for (i=0; i < j->s->img_n; ++i) {+ if (j->img_comp[i].raw_data) {+ STBI_FREE(j->img_comp[i].raw_data);+ j->img_comp[i].raw_data = NULL;+ j->img_comp[i].data = NULL;+ }+ if (j->img_comp[i].raw_coeff) {+ STBI_FREE(j->img_comp[i].raw_coeff);+ j->img_comp[i].raw_coeff = 0;+ j->img_comp[i].coeff = 0;+ }+ if (j->img_comp[i].linebuf) {+ STBI_FREE(j->img_comp[i].linebuf);+ j->img_comp[i].linebuf = NULL;+ }+ }+}++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;++static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp)+{+ int n, decode_n;+ 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;++ if (z->s->img_n == 3 && n < 3)+ decode_n = 1;+ else+ decode_n = z->s->img_n;++ // resample and color-convert+ {+ int k;+ unsigned int i,j;+ stbi_uc *output;+ stbi_uc *coutput[4];++ 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(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) {+ 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 {+ 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; // report original components, not output+ return output;+ }+}++static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)+{+ stbi__jpeg j;+ j.s = s;+ stbi__setup_jpeg(&j);+ return load_jpeg_image(&j, x,y,comp,req_comp);+}++static int stbi__jpeg_test(stbi__context *s)+{+ int r;+ stbi__jpeg j;+ j.s = s;+ stbi__setup_jpeg(&j);+ r = stbi__decode_jpeg_header(&j, STBI__SCAN_type);+ stbi__rewind(s);+ return r;+}++static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp)+{+ if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) {+ stbi__rewind( j->s );+ return 0;+ }+ if (x) *x = j->s->img_x;+ if (y) *y = j->s->img_y;+ if (comp) *comp = j->s->img_n;+ return 1;+}++static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp)+{+ stbi__jpeg j;+ j.s = s;+ return stbi__jpeg_info_raw(&j, x, y, comp);+}+#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)++// 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[288];+ stbi__uint16 value[288];+} 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, 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 stbi_uc stbi__zget8(stbi__zbuf *z)+{+ if (z->zbuffer >= z->zbuffer_end) return 0;+ return *z->zbuffer++;+}++static void stbi__fill_bits(stbi__zbuf *z)+{+ do {+ STBI_ASSERT(z->code_buffer < (1U << z->num_bits));+ 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];+ STBI_ASSERT(z->size[b] == s);+ 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) 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;+ int cur, limit, old_limit;+ z->zout = zout;+ if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG");+ cur = (int) (z->zout - z->zout_start);+ limit = old_limit = (int) (z->zout_end - z->zout_start);+ while (cur + n > limit)+ 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 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 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 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 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 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;++ 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 < hlit + hdist) {+ 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 if (c == 16) {+ c = stbi__zreceive(a,2)+3;+ memset(lencodes+n, lencodes[n-1], c);+ n += c;+ } else if (c == 17) {+ c = stbi__zreceive(a,3)+3;+ memset(lencodes+n, 0, c);+ n += c;+ } else {+ STBI_ASSERT(c == 18);+ c = stbi__zreceive(a,7)+11;+ memset(lencodes+n, 0, c);+ n += c;+ }+ }+ if (n != hlit+hdist) 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_uncomperssed_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;+ }+ STBI_ASSERT(a->num_bits == 0);+ // 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 ((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;+}++// @TODO: should statically initialize these for optimal thread safety+static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32];+static void stbi__init_zdefaults(void)+{+ 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_uncomperssed_block(a)) return 0;+ } else if (type == 3) {+ return 0;+ } else {+ if (type == 1) {+ // use fixed code lengths+ if (!stbi__zdefault_distance[31]) stbi__init_zdefaults();+ if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0;+ if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0;+ } else {+ if (!stbi__compute_huffman_codes(a)) return 0;+ }+ if (!stbi__parse_huffman_block(a)) return 0;+ }+ } while (!final);+ return 1;+}++static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header)+{+ a->zout_start = obuf;+ a->zout = obuf;+ a->zout_end = obuf + olen;+ a->z_expandable = exp;++ return stbi__parse_zlib(a, parse_header);+}++STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen)+{+ stbi__zbuf a;+ char *p = (char *) stbi__malloc(initial_size);+ if (p == NULL) return NULL;+ a.zbuffer = (stbi_uc *) buffer;+ a.zbuffer_end = (stbi_uc *) buffer + len;+ if (stbi__do_zlib(&a, p, initial_size, 1, 1)) {+ if (outlen) *outlen = (int) (a.zout - a.zout_start);+ return a.zout_start;+ } else {+ STBI_FREE(a.zout_start);+ return NULL;+ }+}++STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen)+{+ return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen);+}++STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header)+{+ stbi__zbuf a;+ char *p = (char *) stbi__malloc(initial_size);+ if (p == NULL) return NULL;+ a.zbuffer = (stbi_uc *) buffer;+ a.zbuffer_end = (stbi_uc *) buffer + len;+ if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) {+ if (outlen) *outlen = (int) (a.zout - a.zout_start);+ return a.zout_start;+ } else {+ STBI_FREE(a.zout_start);+ return NULL;+ }+}++STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen)+{+ stbi__zbuf a;+ a.zbuffer = (stbi_uc *) ibuffer;+ a.zbuffer_end = (stbi_uc *) ibuffer + ilen;+ if (stbi__do_zlib(&a, obuffer, olen, 0, 1))+ return (int) (a.zout - a.zout_start);+ else+ return -1;+}++STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen)+{+ stbi__zbuf a;+ char *p = (char *) stbi__malloc(16384);+ if (p == NULL) return NULL;+ a.zbuffer = (stbi_uc *) buffer;+ a.zbuffer_end = (stbi_uc *) buffer+len;+ if (stbi__do_zlib(&a, p, 16384, 1, 0)) {+ if (outlen) *outlen = (int) (a.zout - a.zout_start);+ return a.zout_start;+ } else {+ STBI_FREE(a.zout_start);+ return NULL;+ }+}++STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen)+{+ stbi__zbuf a;+ a.zbuffer = (stbi_uc *) ibuffer;+ a.zbuffer_end = (stbi_uc *) ibuffer + ilen;+ if (stbi__do_zlib(&a, obuffer, olen, 0, 0))+ return (int) (a.zout - a.zout_start);+ else+ return -1;+}+#endif++// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18+// simple implementation+// - only 8-bit samples+// - no CRC checking+// - allocates lots of intermediate memory+// - avoids problem of streaming data between subsystems+// - avoids explicit window management+// performance+// - uses stb_zlib, a PD zlib implementation with fast huffman decoding++#ifndef STBI_NO_PNG+typedef struct+{+ stbi__uint32 length;+ stbi__uint32 type;+} stbi__pngchunk;++static stbi__pngchunk stbi__get_chunk_header(stbi__context *s)+{+ stbi__pngchunk c;+ c.length = stbi__get32be(s);+ c.type = stbi__get32be(s);+ return c;+}++static int stbi__check_png_header(stbi__context *s)+{+ static 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;+} 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 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)+{+ stbi__context *s = a->s;+ stbi__uint32 i,j,stride = x*out_n;+ stbi__uint32 img_len, img_width_bytes;+ int k;+ int img_n = s->img_n; // copy it into a local for later++ STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1);+ a->out = (stbi_uc *) stbi__malloc(x * y * out_n); // extra bytes to write off the end into+ if (!a->out) return stbi__err("outofmem", "Out of memory");++ img_width_bytes = (((img_n * x * depth) + 7) >> 3);+ img_len = (img_width_bytes + 1) * y;+ if (s->img_x == x && s->img_y == y) {+ if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG");+ } else { // interlaced:+ 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 = cur - stride;+ int filter = *raw++;+ int filter_bytes = img_n;+ int width = x;+ if (filter > 4)+ return stbi__err("invalid filter","Corrupt PNG");++ if (depth < 8) {+ STBI_ASSERT(img_width_bytes <= x);+ 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;+ }++ // 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 {+ 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)*img_n;+ #define 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;+ CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); 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] + cur[k-filter_bytes])>>1)); break;+ CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break;+ CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break;+ CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break;+ }+ #undef CASE+ raw += nk;+ } else {+ STBI_ASSERT(img_n+1 == out_n);+ #define CASE(f) \+ case f: \+ for (i=x-1; i >= 1; --i, cur[img_n]=255,raw+=img_n,cur+=out_n,prior+=out_n) \+ for (k=0; k < img_n; ++k)+ switch (filter) {+ CASE(STBI__F_none) cur[k] = raw[k]; break;+ CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-out_n]); 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] + cur[k-out_n])>>1)); break;+ CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],prior[k],prior[k-out_n])); break;+ CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-out_n] >> 1)); break;+ CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-out_n],0,0)); break;+ }+ #undef CASE+ }+ }++ // 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];+ }+ }+ }+ }+ }++ 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)+{+ 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(a->s->img_x * a->s->img_y * out_n);+ 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_n + out_x*out_n,+ a->out + (j*x+i)*out_n, out_n);+ }+ }+ 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__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(pixel_count * pal_img_n);+ 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 = 0;+static int stbi__de_iphone_flag = 0;++STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply)+{+ stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply;+}++STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert)+{+ stbi__de_iphone_flag = flag_true_if_should_convert;+}++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) {+ p[0] = p[2] * 255 / a;+ p[1] = p[1] * 255 / a;+ p[2] = t * 255 / 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) (((a) << 24) + ((b) << 16) + ((c) << 8) + (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];+ stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0;+ int first=1,k,interlace=0, color=0, depth=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); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");+ s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)");+ depth = stbi__get8(s); if (depth != 1 && depth != 2 && depth != 4 && depth != 8) return stbi__err("1/2/4/8-bit only","PNG not supported: 1/2/4/8-bit only");+ color = stbi__get8(s); if (color > 6) 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;+ for (k=0; k < s->img_n; ++k)+ tc[k] = (stbi_uc) (stbi__get16be(s) & 255) * stbi__depth_scale_table[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 * 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, depth, color, interlace)) return 0;+ if (has_trans)+ 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;+ }+ STBI_FREE(z->expanded); z->expanded = NULL;+ 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 unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp)+{+ unsigned char *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)) {+ result = p->out;+ p->out = NULL;+ if (req_comp && req_comp != p->s->img_out_n) {+ result = stbi__convert_format(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_out_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 unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)+{+ stbi__png p;+ p.s = s;+ return stbi__do_png(&p, x,y,comp,req_comp);+}++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);+}+#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, z >>= 1;+ return n;+}++static int stbi__bitcount(unsigned int a)+{+ a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2+ a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4+ a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits+ a = (a + (a >> 8)); // max 16 per 8 bits+ a = (a + (a >> 16)); // max 32 per 8 bits+ return a & 0xff;+}++static int stbi__shiftsigned(int v, int shift, int bits)+{+ int result;+ int z=0;++ if (shift < 0) v <<= -shift;+ else v >>= shift;+ result = v;++ z = bits;+ while (z < 8) {+ result += v >> z;+ z += bits;+ }+ return result;+}++typedef struct+{+ int bpp, offset, hsz;+ unsigned int mr,mg,mb,ma, all_a;+} stbi__bmp_data;++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);+ + 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 (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit");+ if (hsz != 12) {+ int compress = stbi__get32le(s);+ if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE");+ 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) {+ info->mr = info->mg = info->mb = 0;+ if (compress == 0) {+ 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 {+ info->mr = 31u << 10;+ info->mg = 31u << 5;+ info->mb = 31u << 0;+ }+ } else if (compress == 3) {+ info->mr = stbi__get32le(s);+ info->mg = stbi__get32le(s);+ info->mb = stbi__get32le(s);+ // 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 {+ 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);+ 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 stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)+{+ 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;++ 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);++ 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 - 14 - 24) / 3;+ } else {+ if (info.bpp < 16)+ psize = (info.offset - 14 - info.hsz) >> 2;+ }++ 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++ out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y);+ 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 - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4));+ 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;+ 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 - 14 - 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);+ }+ 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));+ 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;+ // else: fall-through+ case 15: if(is_rgb16) *is_rgb16 = 1;+ return STBI_rgb;+ case 24: // fall-through+ 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+void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out)+{+ stbi__uint16 px = 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] = (r * 255)/31;+ out[1] = (g * 255)/31;+ out[2] = (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 stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)+{+ // 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];+ int RLE_count = 0;+ int RLE_repeating = 0;+ int read_next_pixel = 1;++ // do a tiny bit of precessing+ if ( tga_image_type >= 8 )+ {+ tga_image_type -= 8;+ tga_is_RLE = 1;+ }+ tga_inverted = 1 - ((tga_inverted >> 5) & 1);++ // If I'm paletted, then I'll use the number of bits from the palette+ if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16);+ else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16);++ if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency+ return stbi__errpuc("bad format", "Can't find out TGA pixelformat");++ // tga info+ *x = tga_width;+ *y = tga_height;+ if (comp) *comp = tga_comp;++ tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp );+ 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)+ {+ // any data to skip? (offset usually = 0)+ stbi__skip(s, tga_palette_start );+ // load the palette+ tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_comp );+ if (!tga_palette) {+ STBI_FREE(tga_data);+ return stbi__errpuc("outofmem", "Out of memory");+ }+ if (tga_rgb16) {+ stbi_uc *pal_entry = tga_palette;+ STBI_ASSERT(tga_comp == STBI_rgb);+ for (i=0; i < tga_palette_len; ++i) {+ stbi__tga_read_rgb16(s, pal_entry);+ pal_entry += tga_comp;+ }+ } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) {+ STBI_FREE(tga_data);+ STBI_FREE(tga_palette);+ return stbi__errpuc("bad palette", "Corrupt TGA");+ }+ }+ // load the data+ for (i=0; i < tga_width * tga_height; ++i)+ {+ // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk?+ if ( tga_is_RLE )+ {+ if ( RLE_count == 0 )+ {+ // yep, get the next byte as a RLE command+ int RLE_cmd = stbi__get8(s);+ RLE_count = 1 + (RLE_cmd & 127);+ RLE_repeating = RLE_cmd >> 7;+ read_next_pixel = 1;+ } else if ( !RLE_repeating )+ {+ read_next_pixel = 1;+ }+ } else+ {+ read_next_pixel = 1;+ }+ // OK, if I need to read a pixel, do it now+ if ( read_next_pixel )+ {+ // load however much data we did have+ if ( tga_indexed )+ {+ // read in index, then perform the lookup+ int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s);+ if ( pal_idx >= tga_palette_len ) {+ // invalid index+ pal_idx = 0;+ }+ pal_idx *= tga_comp;+ for (j = 0; j < tga_comp; ++j) {+ raw_data[j] = tga_palette[pal_idx+j];+ }+ } else if(tga_rgb16) {+ STBI_ASSERT(tga_comp == STBI_rgb);+ stbi__tga_read_rgb16(s, raw_data);+ } else {+ // read in the data raw+ for (j = 0; j < tga_comp; ++j) {+ raw_data[j] = stbi__get8(s);+ }+ }+ // clear the reading flag for the next pixel+ read_next_pixel = 0;+ } // end of reading a pixel++ // copy data+ for (j = 0; j < tga_comp; ++j)+ tga_data[i*tga_comp+j] = raw_data[j];++ // in case we're in RLE mode, keep counting down+ --RLE_count;+ }+ // do I need to invert the image?+ if ( tga_inverted )+ {+ for (j = 0; j*2 < tga_height; ++j)+ {+ int index1 = j * tga_width * tga_comp;+ int index2 = (tga_height - 1 - j) * tga_width * tga_comp;+ for (i = tga_width * tga_comp; i > 0; --i)+ {+ unsigned char temp = tga_data[index1];+ tga_data[index1] = tga_data[index2];+ tga_data[index2] = temp;+ ++index1;+ ++index2;+ }+ }+ }+ // clear my palette, if I had one+ if ( tga_palette != NULL )+ {+ STBI_FREE( tga_palette );+ }+ }++ // swap RGB - if the source data was RGB16, it already is in the right order+ if (tga_comp >= 3 && !tga_rgb16)+ {+ unsigned char* tga_pixel = tga_data;+ for (i=0; i < tga_width * tga_height; ++i)+ {+ unsigned char temp = tga_pixel[0];+ tga_pixel[0] = tga_pixel[2];+ tga_pixel[2] = temp;+ tga_pixel += tga_comp;+ }+ }++ // convert to target component count+ if (req_comp && req_comp != tga_comp)+ tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height);++ // the things I do to get rid of an error message, and yet keep+ // Microsoft's C compilers happy... [8^(+ tga_palette_start = tga_palette_len = tga_palette_bits =+ tga_x_origin = tga_y_origin = 0;+ // 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 stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)+{+ int pixelCount;+ int channelCount, compression;+ int channel, i, count, len;+ int bitdepth;+ int w,h;+ stbi_uc *out;++ // 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);++ // 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");++ // Create the destination image.+ 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 preceeded 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.+ count = 0;+ while (count < pixelCount) {+ len = stbi__get8(s);+ if (len == 128) {+ // No-op.+ } else if (len < 128) {+ // Copy next len+1 bytes literally.+ len++;+ 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 ^= 0x0FF;+ len += 2;+ val = stbi__get8(s);+ count += len;+ while (len) {+ *p = val;+ p += 4;+ len--;+ }+ }+ }+ }+ }++ } 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 value for each pixel in the image.++ // Read the data by channel.+ for (channel = 0; channel < 4; channel++) {+ stbi_uc *p;++ p = out + channel;+ if (channel >= channelCount) {+ // Fill this channel with default data.+ stbi_uc val = channel == 3 ? 255 : 0;+ for (i = 0; i < pixelCount; i++, p += 4)+ *p = val;+ } else {+ // Read the data.+ if (bitdepth == 16) {+ 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);+ }+ }+ }+ }++ if (req_comp && req_comp != 4) {+ 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; y<height; ++y) {+ int packet_idx;++ for(packet_idx=0; packet_idx < num_packets; ++packet_idx) {+ stbi__pic_packet *packet = &packets[packet_idx];+ stbi_uc *dest = result+y*width*4;++ switch (packet->type) {+ default:+ return stbi__errpuc("bad format","packet has bad compression type");++ case 0: {//uncompressed+ int x;++ for(x=0;x<width;++x, dest+=4)+ if (!stbi__readval(s,packet->channel,dest))+ return 0;+ break;+ }++ case 1://Pure RLE+ {+ int left=width, i;++ 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; i<count; ++i,dest+=4)+ stbi__copyval(packet->channel,dest,value);+ left -= count;+ }+ }+ break;++ case 2: {//Mixed RLE+ int left=width;+ while (left>0) {+ int count = stbi__get8(s), i;+ if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)");++ 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;i<count;++i, dest += 4)+ stbi__copyval(packet->channel,dest,value);+ } else { // Raw+ ++count;+ if (count>left) return stbi__errpuc("bad file","scanline overrun");++ for(i=0;i<count;++i, dest+=4)+ if (!stbi__readval(s,packet->channel,dest))+ return 0;+ }+ left-=count;+ }+ break;+ }+ }+ }+ }++ return result;+}++static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp)+{+ stbi_uc *result;+ int i, x,y;++ for (i=0; i<92; ++i)+ stbi__get8(s);++ x = stbi__get16be(s);+ y = stbi__get16be(s);+ if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)");+ if ((1 << 28) / x < y) return stbi__errpuc("too large", "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(x*y*4);+ memset(result, 0xff, x*y*4);++ if (!stbi__pic_load_core(s,x,y,comp, result)) {+ STBI_FREE(result);+ result=0;+ }+ *px = x;+ *py = y;+ if (req_comp == 0) req_comp = *comp;+ result=stbi__convert_format(result,4,req_comp,x,y);++ return result;+}++static int stbi__pic_test(stbi__context *s)+{+ int r = stbi__pic_test_core(s);+ stbi__rewind(s);+ return r;+}+#endif++// *************************************************************************************************+// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb++#ifndef STBI_NO_GIF+typedef struct+{+ stbi__int16 prefix;+ stbi_uc first;+ stbi_uc suffix;+} stbi__gif_lzw;++typedef struct+{+ int w,h;+ stbi_uc *out, *old_out; // output buffer (always 4 components)+ int flags, bgindex, ratio, transparent, eflags, delay;+ stbi_uc pal[256][4];+ stbi_uc lpal[256][4];+ stbi__gif_lzw codes[4096];+ 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;+} 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 (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;+ if (!stbi__gif_header(s, &g, comp, 1)) {+ stbi__rewind( s );+ return 0;+ }+ if (x) *x = g.w;+ if (y) *y = g.h;+ return 1;+}++static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)+{+ stbi_uc *p, *c;++ // 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;++ p = &g->out[g->cur_x + g->cur_y];+ c = &g->color_table[g->codes[code].suffix * 4];++ if (c[3] >= 128) {+ 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 > 4096) 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");+ }+ }+ }+}++static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1)+{+ int x, y;+ stbi_uc *c = g->pal[g->bgindex];+ for (y = y0; y < y1; y += 4 * g->w) {+ for (x = x0; x < x1; x += 4) {+ stbi_uc *p = &g->out[y + x];+ p[0] = c[2];+ p[1] = c[1];+ p[2] = c[0];+ p[3] = 0;+ }+ }+}++// this function is designed to support animated gifs, although stb_image doesn't support it+static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp)+{+ int i;+ stbi_uc *prev_out = 0;++ if (g->out == 0 && !stbi__gif_header(s, g, comp,0))+ return 0; // stbi__g_failure_reason set by stbi__gif_header++ prev_out = g->out;+ g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h);+ if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory");++ switch ((g->eflags & 0x1C) >> 2) {+ case 0: // unspecified (also always used on 1st frame)+ stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h);+ break;+ case 1: // do not dispose+ if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h);+ g->old_out = prev_out;+ break;+ case 2: // dispose to background+ if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h);+ stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y);+ break;+ case 3: // dispose to previous+ if (g->old_out) {+ for (i = g->start_y; i < g->max_y; i += 4 * g->w)+ memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x);+ }+ break;+ }++ for (;;) {+ switch (stbi__get8(s)) {+ case 0x2C: /* Image Descriptor */+ {+ int prev_trans = -1;+ 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;++ 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) {+ if (g->transparent >= 0 && (g->eflags & 0x01)) {+ prev_trans = g->pal[g->transparent][3];+ g->pal[g->transparent][3] = 0;+ }+ 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 == NULL) return NULL;++ if (prev_trans != -1)+ g->pal[g->transparent][3] = (stbi_uc) prev_trans;++ return o;+ }++ case 0x21: // Comment Extension.+ {+ int len;+ if (stbi__get8(s) == 0xF9) { // Graphic Control Extension.+ len = stbi__get8(s);+ if (len == 4) {+ g->eflags = stbi__get8(s);+ g->delay = stbi__get16le(s);+ g->transparent = stbi__get8(s);+ } 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");+ }+ }++ STBI_NOTUSED(req_comp);+}++static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)+{+ stbi_uc *u = 0;+ stbi__gif g;+ memset(&g, 0, sizeof(g));++ u = stbi__gif_load_next(s, &g, comp, req_comp);+ if (u == (stbi_uc *) s) u = 0; // end of animated gif marker+ if (u) {+ *x = g.w;+ *y = g.h;+ if (req_comp && req_comp != 4)+ u = stbi__convert_format(u, 4, req_comp, g.w, g.h);+ }+ else if (g.out)+ STBI_FREE(g.out);++ 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 = "#?RADIANCE\n";+ int i;+ for (i=0; signature[i]; ++i)+ if (stbi__get8(s) != signature[i])+ return 0;+ return 1;+}++static int stbi__hdr_test(stbi__context* s)+{+ int r = stbi__hdr_test_core(s);+ 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)+{+ 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;+++ // Check identifier+ if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 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);++ *x = width;+ *y = height;++ if (comp) *comp = 3;+ if (req_comp == 0) req_comp = 3;++ // Read data+ hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float));++ // 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(width * 4);++ for (k = 0; k < 4; ++k) {+ i = 0;+ while (i < width) {+ count = stbi__get8(s);+ if (count > 128) {+ // Run+ value = stbi__get8(s);+ count -= 128;+ for (z = 0; z < count; ++z)+ scanline[i++ * 4 + k] = value;+ } else {+ // Dump+ 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);+ }+ 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;++ 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);+ stbi__rewind( s );+ if (p == NULL)+ return 0;+ *x = s->img_x;+ *y = s->img_y;+ *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;+ 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);+ if (stbi__get16be(s) != 8) {+ stbi__rewind( s );+ return 0;+ }+ if (stbi__get16be(s) != 3) {+ stbi__rewind( s );+ return 0;+ }+ *comp = 4;+ 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;+ stbi__pic_packet packets[10];++ 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)+// Does not support 16-bit-per-channel++#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 stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp)+{+ stbi_uc *out;+ if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n))+ return 0;+ *x = s->img_x;+ *y = s->img_y;+ *comp = s->img_n;++ out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y);+ if (!out) return stbi__errpuc("outofmem", "Out of memory");+ stbi__getn(s, out, s->img_n * s->img_x * s->img_y);++ if (req_comp && req_comp != s->img_n) {+ out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y);+ if (out == NULL) return out; // stbi__convert_format frees input on failure+ }+ return out;+}++static int stbi__pnm_isspace(char c)+{+ return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r';+}++static void stbi__pnm_skip_whitespace(stbi__context *s, char *c)+{+ for (;;) {+ while (!stbi__at_eof(s) && stbi__pnm_isspace(*c))+ *c = (char) stbi__get8(s);++ if (stbi__at_eof(s) || *c != '#')+ break;++ while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' )+ *c = (char) stbi__get8(s);+ }+}++static int stbi__pnm_isdigit(char c)+{+ return c >= '0' && c <= '9';+}++static int stbi__pnm_getinteger(stbi__context *s, char *c)+{+ int value = 0;++ while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) {+ value = value*10 + (*c - '0');+ *c = (char) stbi__get8(s);+ }++ return value;+}++static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp)+{+ int maxv;+ char c, p, t;++ stbi__rewind( s );++ // Get identifier+ p = (char) stbi__get8(s);+ t = (char) stbi__get8(s);+ if (p != 'P' || (t != '5' && t != '6')) {+ stbi__rewind( s );+ return 0;+ }++ *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm++ c = (char) stbi__get8(s);+ stbi__pnm_skip_whitespace(s, &c);++ *x = stbi__pnm_getinteger(s, &c); // read width+ stbi__pnm_skip_whitespace(s, &c);++ *y = stbi__pnm_getinteger(s, &c); // read height+ stbi__pnm_skip_whitespace(s, &c);++ maxv = stbi__pnm_getinteger(s, &c); // read max value++ if (maxv > 255)+ return stbi__err("max value > 255", "PPM image not 8-bit");+ else+ return 1;+}+#endif++static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp)+{+ #ifndef STBI_NO_JPEG+ if (stbi__jpeg_info(s, x, y, comp)) return 1;+ #endif++ #ifndef STBI_NO_PNG+ if (stbi__png_info(s, x, y, comp)) return 1;+ #endif++ #ifndef STBI_NO_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");+}++#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;+}+#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);+}++#endif // STB_IMAGE_IMPLEMENTATION++/*+ revision history:+ 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+*/
+ nanovg/src/stb_truetype.h view
@@ -0,0 +1,3249 @@+// stb_truetype.h - v1.09 - public domain+// authored from 2009-2015 by Sean Barrett / RAD Game Tools+//+// This library processes TrueType files:+// parse files+// extract glyph metrics+// extract glyph shapes+// render glyphs to one-channel bitmaps with antialiasing (box filter)+//+// Todo:+// non-MS cmaps+// crashproof on bad data+// hinting? (no longer patented)+// cleartype-style AA?+// optimize: use simple memory allocator for intermediates+// optimize: build edge-list directly from curves+// optimize: rasterize directly from curves?+//+// ADDITIONAL CONTRIBUTORS+//+// Mikko Mononen: compound shape support, more cmap formats+// Tor Andersson: kerning, subpixel rendering+//+// Bug/warning reports/fixes:+// "Zer" on mollyrocket (with fix)+// Cass Everitt+// stoiko (Haemimont Games)+// Brian Hook +// Walter van Niftrik+// David Gow+// David Given+// Ivan-Assen Ivanov+// Anthony Pesch+// Johan Duparc+// Hou Qiming+// Fabian "ryg" Giesen+// Martins Mozeiko+// Cap Petschulat+// Omar Cornut+// github:aloucks+// Peter LaValle+// Sergey Popov+// Giumo X. Clanjor+// Higor Euripedes+// Thomas Fields+// Derek Vinyard+//+// Misc other:+// Ryan Gordon+//+// VERSION HISTORY+//+// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly+// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges+// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;+// variant PackFontRanges to pack and render in separate phases;+// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);+// fixed an assert() bug in the new rasterizer+// replace assert() with STBTT_assert() in new rasterizer+// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)+// also more precise AA rasterizer, except if shapes overlap+// remove need for STBTT_sort+// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC+// 1.04 (2015-04-15) typo in example+// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes+//+// Full history can be found at the end of this file.+//+// LICENSE+//+// This software is in the public domain. Where that dedication is not+// recognized, you are granted a perpetual, irrevocable license to copy,+// distribute, and modify this file as you see fit.+//+// USAGE+//+// Include this file in whatever places neeed to refer to it. In ONE C/C+++// file, write:+// #define STB_TRUETYPE_IMPLEMENTATION+// before the #include of this file. This expands out the actual+// implementation into that C/C++ file.+//+// To make the implementation private to the file that generates the implementation,+// #define STBTT_STATIC+//+// Simple 3D API (don't ship this, but it's fine for tools and quick start)+// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture+// stbtt_GetBakedQuad() -- compute quad to draw for a given char+//+// Improved 3D API (more shippable):+// #include "stb_rect_pack.h" -- optional, but you really want it+// stbtt_PackBegin()+// stbtt_PackSetOversample() -- for improved quality on small fonts+// stbtt_PackFontRanges() -- pack and renders+// stbtt_PackEnd()+// stbtt_GetPackedQuad()+//+// "Load" a font file from a memory buffer (you have to keep the buffer loaded)+// stbtt_InitFont()+// stbtt_GetFontOffsetForIndex() -- use for TTC font collections+//+// Render a unicode codepoint to a bitmap+// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap+// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide+// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be+//+// Character advance/positioning+// stbtt_GetCodepointHMetrics()+// stbtt_GetFontVMetrics()+// stbtt_GetCodepointKernAdvance()+//+// Starting with version 1.06, the rasterizer was replaced with a new,+// faster and generally-more-precise rasterizer. The new rasterizer more+// accurately measures pixel coverage for anti-aliasing, except in the case+// where multiple shapes overlap, in which case it overestimates the AA pixel+// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If+// this turns out to be a problem, you can re-enable the old rasterizer with+// #define STBTT_RASTERIZER_VERSION 1+// which will incur about a 15% speed hit.+//+// ADDITIONAL DOCUMENTATION+//+// Immediately after this block comment are a series of sample programs.+//+// After the sample programs is the "header file" section. This section+// includes documentation for each API function.+//+// Some important concepts to understand to use this library:+//+// Codepoint+// Characters are defined by unicode codepoints, e.g. 65 is+// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is+// the hiragana for "ma".+//+// Glyph+// A visual character shape (every codepoint is rendered as+// some glyph)+//+// Glyph index+// A font-specific integer ID representing a glyph+//+// Baseline+// Glyph shapes are defined relative to a baseline, which is the+// bottom of uppercase characters. Characters extend both above+// and below the baseline.+//+// Current Point+// As you draw text to the screen, you keep track of a "current point"+// which is the origin of each character. The current point's vertical+// position is the baseline. Even "baked fonts" use this model.+//+// Vertical Font Metrics+// The vertical qualities of the font, used to vertically position+// and space the characters. See docs for stbtt_GetFontVMetrics.+//+// Font Size in Pixels or Points+// The preferred interface for specifying font sizes in stb_truetype+// is to specify how tall the font's vertical extent should be in pixels.+// If that sounds good enough, skip the next paragraph.+//+// Most font APIs instead use "points", which are a common typographic+// measurement for describing font size, defined as 72 points per inch.+// stb_truetype provides a point API for compatibility. However, true+// "per inch" conventions don't make much sense on computer displays+// since they different monitors have different number of pixels per+// inch. For example, Windows traditionally uses a convention that+// there are 96 pixels per inch, thus making 'inch' measurements have+// nothing to do with inches, and thus effectively defining a point to+// be 1.333 pixels. Additionally, the TrueType font data provides+// an explicit scale factor to scale a given font's glyphs to points,+// but the author has observed that this scale factor is often wrong+// for non-commercial fonts, thus making fonts scaled in points+// according to the TrueType spec incoherently sized in practice.+//+// ADVANCED USAGE+//+// Quality:+//+// - Use the functions with Subpixel at the end to allow your characters+// to have subpixel positioning. Since the font is anti-aliased, not+// hinted, this is very import for quality. (This is not possible with+// baked fonts.)+//+// - Kerning is now supported, and if you're supporting subpixel rendering+// then kerning is worth using to give your text a polished look.+//+// Performance:+//+// - Convert Unicode codepoints to glyph indexes and operate on the glyphs;+// if you don't do this, stb_truetype is forced to do the conversion on+// every call.+//+// - There are a lot of memory allocations. We should modify it to take+// a temp buffer and allocate from the temp buffer (without freeing),+// should help performance a lot.+//+// NOTES+//+// The system uses the raw data found in the .ttf file without changing it+// and without building auxiliary data structures. This is a bit inefficient+// on little-endian systems (the data is big-endian), but assuming you're+// caching the bitmaps or glyph shapes this shouldn't be a big deal.+//+// It appears to be very hard to programmatically determine what font a+// given file is in a general way. I provide an API for this, but I don't+// recommend it.+//+//+// SOURCE STATISTICS (based on v0.6c, 2050 LOC)+//+// Documentation & header file 520 LOC \___ 660 LOC documentation+// Sample code 140 LOC /+// Truetype parsing 620 LOC ---- 620 LOC TrueType+// Software rasterization 240 LOC \ .+// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation+// Bitmap management 100 LOC /+// Baked bitmap interface 70 LOC /+// Font name matching & access 150 LOC ---- 150 +// C runtime library abstraction 60 LOC ---- 60+//+//+// PERFORMANCE MEASUREMENTS FOR 1.06:+//+// 32-bit 64-bit+// Previous release: 8.83 s 7.68 s+// Pool allocations: 7.72 s 6.34 s+// Inline sort : 6.54 s 5.65 s+// New rasterizer : 5.63 s 5.00 s++//////////////////////////////////////////////////////////////////////////////+//////////////////////////////////////////////////////////////////////////////+////+//// SAMPLE PROGRAMS+////+//+// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless+//+#if 0+#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation+#include "stb_truetype.h"++unsigned char ttf_buffer[1<<20];+unsigned char temp_bitmap[512*512];++stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs+GLuint ftex;++void my_stbtt_initfont(void)+{+ fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));+ stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!+ // can free ttf_buffer at this point+ glGenTextures(1, &ftex);+ glBindTexture(GL_TEXTURE_2D, ftex);+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);+ // can free temp_bitmap at this point+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);+}++void my_stbtt_print(float x, float y, char *text)+{+ // assume orthographic projection with units = screen pixels, origin at top left+ glEnable(GL_TEXTURE_2D);+ glBindTexture(GL_TEXTURE_2D, ftex);+ glBegin(GL_QUADS);+ while (*text) {+ if (*text >= 32 && *text < 128) {+ stbtt_aligned_quad q;+ stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9+ glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);+ glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);+ glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);+ glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);+ }+ ++text;+ }+ glEnd();+}+#endif+//+//+//////////////////////////////////////////////////////////////////////////////+//+// Complete program (this compiles): get a single bitmap, print as ASCII art+//+#if 0+#include <stdio.h>+#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation+#include "stb_truetype.h"++char ttf_buffer[1<<25];++int main(int argc, char **argv)+{+ stbtt_fontinfo font;+ unsigned char *bitmap;+ int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);++ fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb"));++ stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));+ bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);++ for (j=0; j < h; ++j) {+ for (i=0; i < w; ++i)+ putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);+ putchar('\n');+ }+ return 0;+}+#endif +//+// Output:+//+// .ii.+// @@@@@@.+// V@Mio@@o+// :i. V@V+// :oM@@M+// :@@@MM@M+// @@o o@M+// :@@. M@M+// @@@o@@@@+// :M@@V:@@.+// +//////////////////////////////////////////////////////////////////////////////+// +// Complete program: print "Hello World!" banner, with bugs+//+#if 0+char buffer[24<<20];+unsigned char screen[20][79];++int main(int arg, char **argv)+{+ stbtt_fontinfo font;+ int i,j,ascent,baseline,ch=0;+ float scale, xpos=2; // leave a little padding in case the character extends left+ char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness++ fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));+ stbtt_InitFont(&font, buffer, 0);++ scale = stbtt_ScaleForPixelHeight(&font, 15);+ stbtt_GetFontVMetrics(&font, &ascent,0,0);+ baseline = (int) (ascent*scale);++ while (text[ch]) {+ int advance,lsb,x0,y0,x1,y1;+ float x_shift = xpos - (float) floor(xpos);+ stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);+ stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);+ stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);+ // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong+ // because this API is really for baking character bitmaps into textures. if you want to render+ // a sequence of characters, you really need to render each bitmap to a temp buffer, then+ // "alpha blend" that into the working buffer+ xpos += (advance * scale);+ if (text[ch+1])+ xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);+ ++ch;+ }++ for (j=0; j < 20; ++j) {+ for (i=0; i < 78; ++i)+ putchar(" .:ioVM@"[screen[j][i]>>5]);+ putchar('\n');+ }++ return 0;+}+#endif+++//////////////////////////////////////////////////////////////////////////////+//////////////////////////////////////////////////////////////////////////////+////+//// INTEGRATION WITH YOUR CODEBASE+////+//// The following sections allow you to supply alternate definitions+//// of C library functions used by stb_truetype.++#ifdef STB_TRUETYPE_IMPLEMENTATION+ // #define your own (u)stbtt_int8/16/32 before including to override this+ #ifndef stbtt_uint8+ typedef unsigned char stbtt_uint8;+ typedef signed char stbtt_int8;+ typedef unsigned short stbtt_uint16;+ typedef signed short stbtt_int16;+ typedef unsigned int stbtt_uint32;+ typedef signed int stbtt_int32;+ #endif++ typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];+ typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];++ // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h+ #ifndef STBTT_ifloor+ #include <math.h>+ #define STBTT_ifloor(x) ((int) floor(x))+ #define STBTT_iceil(x) ((int) ceil(x))+ #endif++ #ifndef STBTT_sqrt+ #include <math.h>+ #define STBTT_sqrt(x) sqrt(x)+ #endif++ // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h+ #ifndef STBTT_malloc+ #include <stdlib.h>+ #define STBTT_malloc(x,u) ((void)(u),malloc(x))+ #define STBTT_free(x,u) ((void)(u),free(x))+ #endif++ #ifndef STBTT_assert+ #include <assert.h>+ #define STBTT_assert(x) assert(x)+ #endif++ #ifndef STBTT_strlen+ #include <string.h>+ #define STBTT_strlen(x) strlen(x)+ #endif++ #ifndef STBTT_memcpy+ #include <memory.h>+ #define STBTT_memcpy memcpy+ #define STBTT_memset memset+ #endif+#endif++///////////////////////////////////////////////////////////////////////////////+///////////////////////////////////////////////////////////////////////////////+////+//// INTERFACE+////+////++#ifndef __STB_INCLUDE_STB_TRUETYPE_H__+#define __STB_INCLUDE_STB_TRUETYPE_H__++#ifdef STBTT_STATIC+#define STBTT_DEF static+#else+#define STBTT_DEF extern+#endif++#ifdef __cplusplus+extern "C" {+#endif++//////////////////////////////////////////////////////////////////////////////+//+// TEXTURE BAKING API+//+// If you use this API, you only have to call two functions ever.+//++typedef struct+{+ unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap+ float xoff,yoff,xadvance;+} stbtt_bakedchar;++STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)+ float pixel_height, // height of font in pixels+ unsigned char *pixels, int pw, int ph, // bitmap to be filled in+ int first_char, int num_chars, // characters to bake+ stbtt_bakedchar *chardata); // you allocate this, it's num_chars long+// if return is positive, the first unused row of the bitmap+// if return is negative, returns the negative of the number of characters that fit+// if return is 0, no characters fit and no rows were used+// This uses a very crappy packing.++typedef struct+{+ float x0,y0,s0,t0; // top-left+ float x1,y1,s1,t1; // bottom-right+} stbtt_aligned_quad;++STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above+ int char_index, // character to display+ float *xpos, float *ypos, // pointers to current position in screen pixel space+ stbtt_aligned_quad *q, // output: quad to draw+ int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier+// Call GetBakedQuad with char_index = 'character - first_char', and it+// creates the quad you need to draw and advances the current position.+//+// The coordinate system used assumes y increases downwards.+//+// Characters will extend both above and below the current position;+// see discussion of "BASELINE" above.+//+// It's inefficient; you might want to c&p it and optimize it.++++//////////////////////////////////////////////////////////////////////////////+//+// NEW TEXTURE BAKING API+//+// This provides options for packing multiple fonts into one atlas, not+// perfectly but better than nothing.++typedef struct+{+ unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap+ float xoff,yoff,xadvance;+ float xoff2,yoff2;+} stbtt_packedchar;++typedef struct stbtt_pack_context stbtt_pack_context;+typedef struct stbtt_fontinfo stbtt_fontinfo;+#ifndef STB_RECT_PACK_VERSION+typedef struct stbrp_rect stbrp_rect;+#endif++STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);+// Initializes a packing context stored in the passed-in stbtt_pack_context.+// Future calls using this context will pack characters into the bitmap passed+// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is+// the distance from one row to the next (or 0 to mean they are packed tightly+// together). "padding" is the amount of padding to leave between each+// character (normally you want '1' for bitmaps you'll use as textures with+// bilinear filtering).+//+// Returns 0 on failure, 1 on success.++STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc);+// Cleans up the packing context and frees all memory.++#define STBTT_POINT_SIZE(x) (-(x))++STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,+ int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);+// Creates character bitmaps from the font_index'th font found in fontdata (use+// font_index=0 if you don't know what that is). It creates num_chars_in_range+// bitmaps for characters with unicode values starting at first_unicode_char_in_range+// and increasing. Data for how to render them is stored in chardata_for_range;+// pass these to stbtt_GetPackedQuad to get back renderable quads.+//+// font_size is the full height of the character from ascender to descender,+// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed+// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE()+// and pass that result as 'font_size':+// ..., 20 , ... // font max minus min y is 20 pixels tall+// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall++typedef struct+{+ float font_size;+ int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint+ int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints+ int num_chars;+ stbtt_packedchar *chardata_for_range; // output+ unsigned char h_oversample, v_oversample; // don't set these, they're used internally+} stbtt_pack_range;++STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);+// Creates character bitmaps from multiple ranges of characters stored in+// ranges. This will usually create a better-packed bitmap than multiple+// calls to stbtt_PackFontRange. Note that you can call this multiple+// times within a single PackBegin/PackEnd.++STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);+// Oversampling a font increases the quality by allowing higher-quality subpixel+// positioning, and is especially valuable at smaller text sizes.+//+// This function sets the amount of oversampling for all following calls to+// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given+// pack context. The default (no oversampling) is achieved by h_oversample=1+// and v_oversample=1. The total number of pixels required is+// h_oversample*v_oversample larger than the default; for example, 2x2+// oversampling requires 4x the storage of 1x1. For best results, render+// oversampled textures with bilinear filtering. Look at the readme in+// stb/tests/oversample for information about oversampled fonts+//+// To use with PackFontRangesGather etc., you must set it before calls+// call to PackFontRangesGatherRects.++STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above+ int char_index, // character to display+ float *xpos, float *ypos, // pointers to current position in screen pixel space+ stbtt_aligned_quad *q, // output: quad to draw+ int align_to_integer);++STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);+STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects);+STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);+// Calling these functions in sequence is roughly equivalent to calling+// stbtt_PackFontRanges(). If you more control over the packing of multiple+// fonts, or if you want to pack custom data into a font texture, take a look+// at the source to of stbtt_PackFontRanges() and create a custom version +// using these functions, e.g. call GatherRects multiple times,+// building up a single array of rects, then call PackRects once,+// then call RenderIntoRects repeatedly. This may result in a+// better packing than calling PackFontRanges multiple times+// (or it may not).++// this is an opaque structure that you shouldn't mess with which holds+// all the context needed from PackBegin to PackEnd.+struct stbtt_pack_context {+ void *user_allocator_context;+ void *pack_info;+ int width;+ int height;+ int stride_in_bytes;+ int padding;+ unsigned int h_oversample, v_oversample;+ unsigned char *pixels;+ void *nodes;+};++//////////////////////////////////////////////////////////////////////////////+//+// FONT LOADING+//+//++STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);+// Each .ttf/.ttc file may have more than one font. Each font has a sequential+// index number starting from 0. Call this function to get the font offset for+// a given index; it returns -1 if the index is out of range. A regular .ttf+// file will only define one font and it always be at offset 0, so it will+// return '0' for index 0, and -1 for all other indices. You can just skip+// this step if you know it's that kind of font.+++// The following structure is defined publically so you can declare one on+// the stack or as a global or etc, but you should treat it as opaque.+typedef struct stbtt_fontinfo+{+ void * userdata;+ unsigned char * data; // pointer to .ttf file+ int fontstart; // offset of start of font++ int numGlyphs; // number of glyphs, needed for range checking++ int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf+ int index_map; // a cmap mapping for our chosen character encoding+ int indexToLocFormat; // format needed to map from glyph index to glyph+} stbtt_fontinfo;++STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);+// Given an offset into the file that defines a font, this function builds+// the necessary cached info for the rest of the system. You must allocate+// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't+// need to do anything special to free it, because the contents are pure+// value data with no additional data structures. Returns 0 on failure.+++//////////////////////////////////////////////////////////////////////////////+//+// CHARACTER TO GLYPH-INDEX CONVERSIOn++STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);+// If you're going to perform multiple operations on the same character+// and you want a speed-up, call this function with the character you're+// going to process, then use glyph-based functions instead of the+// codepoint-based functions.+++//////////////////////////////////////////////////////////////////////////////+//+// CHARACTER PROPERTIES+//++STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);+// computes a scale factor to produce a font whose "height" is 'pixels' tall.+// Height is measured as the distance from the highest ascender to the lowest+// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics+// and computing:+// scale = pixels / (ascent - descent)+// so if you prefer to measure height by the ascent only, use a similar calculation.++STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);+// computes a scale factor to produce a font whose EM size is mapped to+// 'pixels' tall. This is probably what traditional APIs compute, but+// I'm not positive.++STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);+// ascent is the coordinate above the baseline the font extends; descent+// is the coordinate below the baseline the font extends (i.e. it is typically negative)+// lineGap is the spacing between one row's descent and the next row's ascent...+// so you should advance the vertical position by "*ascent - *descent + *lineGap"+// these are expressed in unscaled coordinates, so you must multiply by+// the scale factor for a given size++STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);+// the bounding box around all possible characters++STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);+// leftSideBearing is the offset from the current horizontal position to the left edge of the character+// advanceWidth is the offset from the current horizontal position to the next horizontal position+// these are expressed in unscaled coordinates++STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);+// an additional amount to add to the 'advance' value between ch1 and ch2++STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);+// Gets the bounding box of the visible part of the glyph, in unscaled coordinates++STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);+STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);+STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);+// as above, but takes one or more glyph indices for greater efficiency+++//////////////////////////////////////////////////////////////////////////////+//+// GLYPH SHAPES (you probably don't need these, but they have to go before+// the bitmaps for C declaration-order reasons)+//++#ifndef STBTT_vmove // you can predefine these to use different values (but why?)+ enum {+ STBTT_vmove=1,+ STBTT_vline,+ STBTT_vcurve+ };+#endif++#ifndef stbtt_vertex // you can predefine this to use different values+ // (we share this with other code at RAD)+ #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file+ typedef struct+ {+ stbtt_vertex_type x,y,cx,cy;+ unsigned char type,padding;+ } stbtt_vertex;+#endif++STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);+// returns non-zero if nothing is drawn for this glyph++STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);+STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);+// returns # of vertices and fills *vertices with the pointer to them+// these are expressed in "unscaled" coordinates+//+// The shape is a series of countours. Each one starts with+// a STBTT_moveto, then consists of a series of mixed+// STBTT_lineto and STBTT_curveto segments. A lineto+// draws a line from previous endpoint to its x,y; a curveto+// draws a quadratic bezier from previous endpoint to+// its x,y, using cx,cy as the bezier control point.++STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);+// frees the data allocated above++//////////////////////////////////////////////////////////////////////////////+//+// BITMAP RENDERING+//++STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);+// frees the bitmap allocated below++STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);+// allocates a large-enough single-channel 8bpp bitmap and renders the+// specified character/glyph at the specified scale into it, with+// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).+// *width & *height are filled out with the width & height of the bitmap,+// which is stored left-to-right, top-to-bottom.+//+// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap++STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);+// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel+// shift for the character++STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);+// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap+// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap+// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the+// width and height and positioning info for it first.++STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);+// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel+// shift for the character++STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);+// get the bbox of the bitmap centered around the glyph origin; so the+// bitmap width is ix1-ix0, height is iy1-iy0, and location to place+// the bitmap top left is (leftSideBearing*scale,iy0).+// (Note that the bitmap uses y-increases-down, but the shape uses+// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)++STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);+// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel+// shift for the character++// the following functions are equivalent to the above functions, but operate+// on glyph indices instead of Unicode codepoints (for efficiency)+STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);+STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);+STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);+STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);+STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);+STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);+++// @TODO: don't expose this structure+typedef struct+{+ int w,h,stride;+ unsigned char *pixels;+} stbtt__bitmap;++// rasterize a shape with quadratic beziers into a bitmap+STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into+ float flatness_in_pixels, // allowable error of curve in pixels+ stbtt_vertex *vertices, // array of vertices defining shape+ int num_verts, // number of vertices in above array+ float scale_x, float scale_y, // scale applied to input vertices+ float shift_x, float shift_y, // translation applied to input vertices+ int x_off, int y_off, // another translation applied to input+ int invert, // if non-zero, vertically flip shape+ void *userdata); // context for to STBTT_MALLOC++//////////////////////////////////////////////////////////////////////////////+//+// Finding the right font...+//+// You should really just solve this offline, keep your own tables+// of what font is what, and don't try to get it out of the .ttf file.+// That's because getting it out of the .ttf file is really hard, because+// the names in the file can appear in many possible encodings, in many+// possible languages, and e.g. if you need a case-insensitive comparison,+// the details of that depend on the encoding & language in a complex way+// (actually underspecified in truetype, but also gigantic).+//+// But you can use the provided functions in two possible ways:+// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on+// unicode-encoded names to try to find the font you want;+// you can run this before calling stbtt_InitFont()+//+// stbtt_GetFontNameString() lets you get any of the various strings+// from the file yourself and do your own comparisons on them.+// You have to have called stbtt_InitFont() first.+++STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);+// returns the offset (not index) of the font that matches, or -1 if none+// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".+// if you use any other flag, use a font name like "Arial"; this checks+// the 'macStyle' header field; i don't know if fonts set this consistently+#define STBTT_MACSTYLE_DONTCARE 0+#define STBTT_MACSTYLE_BOLD 1+#define STBTT_MACSTYLE_ITALIC 2+#define STBTT_MACSTYLE_UNDERSCORE 4+#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0++STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);+// returns 1/0 whether the first string interpreted as utf8 is identical to+// the second string interpreted as big-endian utf16... useful for strings from next func++STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);+// returns the string (which may be big-endian double byte, e.g. for unicode)+// and puts the length in bytes in *length.+//+// some of the values for the IDs are below; for more see the truetype spec:+// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html+// http://www.microsoft.com/typography/otspec/name.htm++enum { // platformID+ STBTT_PLATFORM_ID_UNICODE =0,+ STBTT_PLATFORM_ID_MAC =1,+ STBTT_PLATFORM_ID_ISO =2,+ STBTT_PLATFORM_ID_MICROSOFT =3+};++enum { // encodingID for STBTT_PLATFORM_ID_UNICODE+ STBTT_UNICODE_EID_UNICODE_1_0 =0,+ STBTT_UNICODE_EID_UNICODE_1_1 =1,+ STBTT_UNICODE_EID_ISO_10646 =2,+ STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,+ STBTT_UNICODE_EID_UNICODE_2_0_FULL=4+};++enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT+ STBTT_MS_EID_SYMBOL =0,+ STBTT_MS_EID_UNICODE_BMP =1,+ STBTT_MS_EID_SHIFTJIS =2,+ STBTT_MS_EID_UNICODE_FULL =10+};++enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes+ STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4,+ STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5,+ STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6,+ STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7+};++enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...+ // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs+ STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410,+ STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411,+ STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412,+ STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419,+ STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409,+ STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D+};++enum { // languageID for STBTT_PLATFORM_ID_MAC+ STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11,+ STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23,+ STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32,+ STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 ,+ STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 ,+ STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,+ STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19+};++#ifdef __cplusplus+}+#endif++#endif // __STB_INCLUDE_STB_TRUETYPE_H__++///////////////////////////////////////////////////////////////////////////////+///////////////////////////////////////////////////////////////////////////////+////+//// IMPLEMENTATION+////+////++#ifdef STB_TRUETYPE_IMPLEMENTATION++#ifndef STBTT_MAX_OVERSAMPLE+#define STBTT_MAX_OVERSAMPLE 8+#endif++#if STBTT_MAX_OVERSAMPLE > 255+#error "STBTT_MAX_OVERSAMPLE cannot be > 255"+#endif++typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1];++#ifndef STBTT_RASTERIZER_VERSION+#define STBTT_RASTERIZER_VERSION 2+#endif++//////////////////////////////////////////////////////////////////////////+//+// accessors to parse data from file+//++// on platforms that don't allow misaligned reads, if we want to allow+// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE++#define ttBYTE(p) (* (stbtt_uint8 *) (p))+#define ttCHAR(p) (* (stbtt_int8 *) (p))+#define ttFixed(p) ttLONG(p)++#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE)++ #define ttUSHORT(p) (* (stbtt_uint16 *) (p))+ #define ttSHORT(p) (* (stbtt_int16 *) (p))+ #define ttULONG(p) (* (stbtt_uint32 *) (p))+ #define ttLONG(p) (* (stbtt_int32 *) (p))++#else++ static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }+ static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }+ static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }+ static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }++#endif++#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))+#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3])++static int stbtt__isfont(const stbtt_uint8 *font)+{+ // check the version number+ if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1+ if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this!+ if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF+ if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0+ return 0;+}++// @OPTIMIZE: binary search+static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)+{+ stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);+ stbtt_uint32 tabledir = fontstart + 12;+ stbtt_int32 i;+ for (i=0; i < num_tables; ++i) {+ stbtt_uint32 loc = tabledir + 16*i;+ if (stbtt_tag(data+loc+0, tag))+ return ttULONG(data+loc+8);+ }+ return 0;+}++STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index)+{+ // if it's just a font, there's only one valid index+ if (stbtt__isfont(font_collection))+ return index == 0 ? 0 : -1;++ // check if it's a TTC+ if (stbtt_tag(font_collection, "ttcf")) {+ // version 1?+ if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {+ stbtt_int32 n = ttLONG(font_collection+8);+ if (index >= n)+ return -1;+ return ttULONG(font_collection+12+index*4);+ }+ }+ return -1;+}++STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart)+{+ stbtt_uint8 *data = (stbtt_uint8 *) data2;+ stbtt_uint32 cmap, t;+ stbtt_int32 i,numTables;++ info->data = data;+ info->fontstart = fontstart;++ cmap = stbtt__find_table(data, fontstart, "cmap"); // required+ info->loca = stbtt__find_table(data, fontstart, "loca"); // required+ info->head = stbtt__find_table(data, fontstart, "head"); // required+ info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required+ info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required+ info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required+ info->kern = stbtt__find_table(data, fontstart, "kern"); // not required+ if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx)+ return 0;++ t = stbtt__find_table(data, fontstart, "maxp");+ if (t)+ info->numGlyphs = ttUSHORT(data+t+4);+ else+ info->numGlyphs = 0xffff;++ // find a cmap encoding table we understand *now* to avoid searching+ // later. (todo: could make this installable)+ // the same regardless of glyph.+ numTables = ttUSHORT(data + cmap + 2);+ info->index_map = 0;+ for (i=0; i < numTables; ++i) {+ stbtt_uint32 encoding_record = cmap + 4 + 8 * i;+ // find an encoding we understand:+ switch(ttUSHORT(data+encoding_record)) {+ case STBTT_PLATFORM_ID_MICROSOFT:+ switch (ttUSHORT(data+encoding_record+2)) {+ case STBTT_MS_EID_UNICODE_BMP:+ case STBTT_MS_EID_UNICODE_FULL:+ // MS/Unicode+ info->index_map = cmap + ttULONG(data+encoding_record+4);+ break;+ }+ break;+ case STBTT_PLATFORM_ID_UNICODE:+ // Mac/iOS has these+ // all the encodingIDs are unicode, so we don't bother to check it+ info->index_map = cmap + ttULONG(data+encoding_record+4);+ break;+ }+ }+ if (info->index_map == 0)+ return 0;++ info->indexToLocFormat = ttUSHORT(data+info->head + 50);+ return 1;+}++STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)+{+ stbtt_uint8 *data = info->data;+ stbtt_uint32 index_map = info->index_map;++ stbtt_uint16 format = ttUSHORT(data + index_map + 0);+ if (format == 0) { // apple byte encoding+ stbtt_int32 bytes = ttUSHORT(data + index_map + 2);+ if (unicode_codepoint < bytes-6)+ return ttBYTE(data + index_map + 6 + unicode_codepoint);+ return 0;+ } else if (format == 6) {+ stbtt_uint32 first = ttUSHORT(data + index_map + 6);+ stbtt_uint32 count = ttUSHORT(data + index_map + 8);+ if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)+ return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);+ return 0;+ } else if (format == 2) {+ STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean+ return 0;+ } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges+ stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;+ stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;+ stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);+ stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;++ // do a binary search of the segments+ stbtt_uint32 endCount = index_map + 14;+ stbtt_uint32 search = endCount;++ if (unicode_codepoint > 0xffff)+ return 0;++ // they lie from endCount .. endCount + segCount+ // but searchRange is the nearest power of two, so...+ if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))+ search += rangeShift*2;++ // now decrement to bias correctly to find smallest+ search -= 2;+ while (entrySelector) {+ stbtt_uint16 end;+ searchRange >>= 1;+ end = ttUSHORT(data + search + searchRange*2);+ if (unicode_codepoint > end)+ search += searchRange*2;+ --entrySelector;+ }+ search += 2;++ {+ stbtt_uint16 offset, start;+ stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);++ STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));+ start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);+ if (unicode_codepoint < start)+ return 0;++ offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);+ if (offset == 0)+ return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));++ return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);+ }+ } else if (format == 12 || format == 13) {+ stbtt_uint32 ngroups = ttULONG(data+index_map+12);+ stbtt_int32 low,high;+ low = 0; high = (stbtt_int32)ngroups;+ // Binary search the right group.+ while (low < high) {+ stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high+ stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);+ stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);+ if ((stbtt_uint32) unicode_codepoint < start_char)+ high = mid;+ else if ((stbtt_uint32) unicode_codepoint > end_char)+ low = mid+1;+ else {+ stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);+ if (format == 12)+ return start_glyph + unicode_codepoint-start_char;+ else // format == 13+ return start_glyph;+ }+ }+ return 0; // not found+ }+ // @TODO+ STBTT_assert(0);+ return 0;+}++STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)+{+ return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);+}++static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy)+{+ v->type = type;+ v->x = (stbtt_int16) x;+ v->y = (stbtt_int16) y;+ v->cx = (stbtt_int16) cx;+ v->cy = (stbtt_int16) cy;+}++static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)+{+ int g1,g2;++ if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range+ if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format++ if (info->indexToLocFormat == 0) {+ g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;+ g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;+ } else {+ g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);+ g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);+ }++ return g1==g2 ? -1 : g1; // if length is 0, return -1+}++STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)+{+ int g = stbtt__GetGlyfOffset(info, glyph_index);+ if (g < 0) return 0;++ if (x0) *x0 = ttSHORT(info->data + g + 2);+ if (y0) *y0 = ttSHORT(info->data + g + 4);+ if (x1) *x1 = ttSHORT(info->data + g + 6);+ if (y1) *y1 = ttSHORT(info->data + g + 8);+ return 1;+}++STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)+{+ return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);+}++STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)+{+ stbtt_int16 numberOfContours;+ int g = stbtt__GetGlyfOffset(info, glyph_index);+ if (g < 0) return 1;+ numberOfContours = ttSHORT(info->data + g);+ return numberOfContours == 0;+}++static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off,+ stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy)+{+ if (start_off) {+ if (was_off)+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy);+ } else {+ if (was_off)+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);+ else+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);+ }+ return num_vertices;+}++STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)+{+ stbtt_int16 numberOfContours;+ stbtt_uint8 *endPtsOfContours;+ stbtt_uint8 *data = info->data;+ stbtt_vertex *vertices=0;+ int num_vertices=0;+ int g = stbtt__GetGlyfOffset(info, glyph_index);++ *pvertices = NULL;++ if (g < 0) return 0;++ numberOfContours = ttSHORT(data + g);++ if (numberOfContours > 0) {+ stbtt_uint8 flags=0,flagcount;+ stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0;+ stbtt_int32 x,y,cx,cy,sx,sy, scx,scy;+ stbtt_uint8 *points;+ endPtsOfContours = (data + g + 10);+ ins = ttUSHORT(data + g + 10 + numberOfContours * 2);+ points = data + g + 10 + numberOfContours * 2 + 2 + ins;++ n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);++ m = n + 2*numberOfContours; // a loose bound on how many vertices we might need+ vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);+ if (vertices == 0)+ return 0;++ next_move = 0;+ flagcount=0;++ // in first pass, we load uninterpreted data into the allocated array+ // above, shifted to the end of the array so we won't overwrite it when+ // we create our final data starting from the front++ off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated++ // first load flags++ for (i=0; i < n; ++i) {+ if (flagcount == 0) {+ flags = *points++;+ if (flags & 8)+ flagcount = *points++;+ } else+ --flagcount;+ vertices[off+i].type = flags;+ }++ // now load x coordinates+ x=0;+ for (i=0; i < n; ++i) {+ flags = vertices[off+i].type;+ if (flags & 2) {+ stbtt_int16 dx = *points++;+ x += (flags & 16) ? dx : -dx; // ???+ } else {+ if (!(flags & 16)) {+ x = x + (stbtt_int16) (points[0]*256 + points[1]);+ points += 2;+ }+ }+ vertices[off+i].x = (stbtt_int16) x;+ }++ // now load y coordinates+ y=0;+ for (i=0; i < n; ++i) {+ flags = vertices[off+i].type;+ if (flags & 4) {+ stbtt_int16 dy = *points++;+ y += (flags & 32) ? dy : -dy; // ???+ } else {+ if (!(flags & 32)) {+ y = y + (stbtt_int16) (points[0]*256 + points[1]);+ points += 2;+ }+ }+ vertices[off+i].y = (stbtt_int16) y;+ }++ // now convert them to our format+ num_vertices=0;+ sx = sy = cx = cy = scx = scy = 0;+ for (i=0; i < n; ++i) {+ flags = vertices[off+i].type;+ x = (stbtt_int16) vertices[off+i].x;+ y = (stbtt_int16) vertices[off+i].y;++ if (next_move == i) {+ if (i != 0)+ num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);++ // now start the new one + start_off = !(flags & 1);+ if (start_off) {+ // if we start off with an off-curve point, then when we need to find a point on the curve+ // where we can start, and we need to save some state for when we wraparound.+ scx = x;+ scy = y;+ if (!(vertices[off+i+1].type & 1)) {+ // next point is also a curve point, so interpolate an on-point curve+ sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1;+ sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1;+ } else {+ // otherwise just use the next point as our start point+ sx = (stbtt_int32) vertices[off+i+1].x;+ sy = (stbtt_int32) vertices[off+i+1].y;+ ++i; // we're using point i+1 as the starting point, so skip it+ }+ } else {+ sx = x;+ sy = y;+ }+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0);+ was_off = 0;+ next_move = 1 + ttUSHORT(endPtsOfContours+j*2);+ ++j;+ } else {+ if (!(flags & 1)) { // if it's a curve+ if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);+ cx = x;+ cy = y;+ was_off = 1;+ } else {+ if (was_off)+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);+ else+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);+ was_off = 0;+ }+ }+ }+ num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);+ } else if (numberOfContours == -1) {+ // Compound shapes.+ int more = 1;+ stbtt_uint8 *comp = data + g + 10;+ num_vertices = 0;+ vertices = 0;+ while (more) {+ stbtt_uint16 flags, gidx;+ int comp_num_verts = 0, i;+ stbtt_vertex *comp_verts = 0, *tmp = 0;+ float mtx[6] = {1,0,0,1,0,0}, m, n;+ + flags = ttSHORT(comp); comp+=2;+ gidx = ttSHORT(comp); comp+=2;++ if (flags & 2) { // XY values+ if (flags & 1) { // shorts+ mtx[4] = ttSHORT(comp); comp+=2;+ mtx[5] = ttSHORT(comp); comp+=2;+ } else {+ mtx[4] = ttCHAR(comp); comp+=1;+ mtx[5] = ttCHAR(comp); comp+=1;+ }+ }+ else {+ // @TODO handle matching point+ STBTT_assert(0);+ }+ if (flags & (1<<3)) { // WE_HAVE_A_SCALE+ mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;+ mtx[1] = mtx[2] = 0;+ } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE+ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;+ mtx[1] = mtx[2] = 0;+ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;+ } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO+ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;+ mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;+ mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;+ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;+ }+ + // Find transformation scales.+ m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);+ n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);++ // Get indexed glyph.+ comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);+ if (comp_num_verts > 0) {+ // Transform vertices.+ for (i = 0; i < comp_num_verts; ++i) {+ stbtt_vertex* v = &comp_verts[i];+ stbtt_vertex_type x,y;+ x=v->x; y=v->y;+ v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));+ v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));+ x=v->cx; y=v->cy;+ v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));+ v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));+ }+ // Append vertices.+ tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);+ if (!tmp) {+ if (vertices) STBTT_free(vertices, info->userdata);+ if (comp_verts) STBTT_free(comp_verts, info->userdata);+ return 0;+ }+ if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));+ STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));+ if (vertices) STBTT_free(vertices, info->userdata);+ vertices = tmp;+ STBTT_free(comp_verts, info->userdata);+ num_vertices += comp_num_verts;+ }+ // More components ?+ more = flags & (1<<5);+ }+ } else if (numberOfContours < 0) {+ // @TODO other compound variations?+ STBTT_assert(0);+ } else {+ // numberOfCounters == 0, do nothing+ }++ *pvertices = vertices;+ return num_vertices;+}++STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)+{+ stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);+ if (glyph_index < numOfLongHorMetrics) {+ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index);+ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);+ } else {+ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));+ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));+ }+}++STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)+{+ stbtt_uint8 *data = info->data + info->kern;+ stbtt_uint32 needle, straw;+ int l, r, m;++ // we only look at the first table. it must be 'horizontal' and format 0.+ if (!info->kern)+ return 0;+ if (ttUSHORT(data+2) < 1) // number of tables, need at least 1+ return 0;+ if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format+ return 0;++ l = 0;+ r = ttUSHORT(data+10) - 1;+ needle = glyph1 << 16 | glyph2;+ while (l <= r) {+ m = (l + r) >> 1;+ straw = ttULONG(data+18+(m*6)); // note: unaligned read+ if (needle < straw)+ r = m - 1;+ else if (needle > straw)+ l = m + 1;+ else+ return ttSHORT(data+22+(m*6));+ }+ return 0;+}++STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)+{+ if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs+ return 0;+ return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));+}++STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)+{+ stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);+}++STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)+{+ if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4);+ if (descent) *descent = ttSHORT(info->data+info->hhea + 6);+ if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);+}++STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)+{+ *x0 = ttSHORT(info->data + info->head + 36);+ *y0 = ttSHORT(info->data + info->head + 38);+ *x1 = ttSHORT(info->data + info->head + 40);+ *y1 = ttSHORT(info->data + info->head + 42);+}++STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)+{+ int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);+ return (float) height / fheight;+}++STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)+{+ int unitsPerEm = ttUSHORT(info->data + info->head + 18);+ return pixels / unitsPerEm;+}++STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)+{+ STBTT_free(v, info->userdata);+}++//////////////////////////////////////////////////////////////////////////////+//+// antialiasing software rasterizer+//++STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)+{+ int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning+ if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {+ // e.g. space character+ if (ix0) *ix0 = 0;+ if (iy0) *iy0 = 0;+ if (ix1) *ix1 = 0;+ if (iy1) *iy1 = 0;+ } else {+ // move to integral bboxes (treating pixels as little squares, what pixels get touched)?+ if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x);+ if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y);+ if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x);+ if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y);+ }+}++STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)+{+ stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);+}++STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)+{+ stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1);+}++STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)+{+ stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1);+}++//////////////////////////////////////////////////////////////////////////////+//+// Rasterizer++typedef struct stbtt__hheap_chunk+{+ struct stbtt__hheap_chunk *next;+} stbtt__hheap_chunk;++typedef struct stbtt__hheap+{+ struct stbtt__hheap_chunk *head;+ void *first_free;+ int num_remaining_in_head_chunk;+} stbtt__hheap;++static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)+{+ if (hh->first_free) {+ void *p = hh->first_free;+ hh->first_free = * (void **) p;+ return p;+ } else {+ if (hh->num_remaining_in_head_chunk == 0) {+ int count = (size < 32 ? 2000 : size < 128 ? 800 : 100);+ stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata);+ if (c == NULL)+ return NULL;+ c->next = hh->head;+ hh->head = c;+ hh->num_remaining_in_head_chunk = count;+ }+ --hh->num_remaining_in_head_chunk;+ return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk;+ }+}++static void stbtt__hheap_free(stbtt__hheap *hh, void *p)+{+ *(void **) p = hh->first_free;+ hh->first_free = p;+}++static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata)+{+ stbtt__hheap_chunk *c = hh->head;+ while (c) {+ stbtt__hheap_chunk *n = c->next;+ STBTT_free(c, userdata);+ c = n;+ }+}++typedef struct stbtt__edge {+ float x0,y0, x1,y1;+ int invert;+} stbtt__edge;+++typedef struct stbtt__active_edge+{+ struct stbtt__active_edge *next;+ #if STBTT_RASTERIZER_VERSION==1+ int x,dx;+ float ey;+ int direction;+ #elif STBTT_RASTERIZER_VERSION==2+ float fx,fdx,fdy;+ float direction;+ float sy;+ float ey;+ #else+ #error "Unrecognized value of STBTT_RASTERIZER_VERSION"+ #endif+} stbtt__active_edge;++#if STBTT_RASTERIZER_VERSION == 1+#define STBTT_FIXSHIFT 10+#define STBTT_FIX (1 << STBTT_FIXSHIFT)+#define STBTT_FIXMASK (STBTT_FIX-1)++static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)+{+ stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);+ float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);+ STBTT_assert(z != NULL);+ if (!z) return z;+ + // round dx down to avoid overshooting+ if (dxdy < 0)+ z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy);+ else+ z->dx = STBTT_ifloor(STBTT_FIX * dxdy);++ z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount+ z->x -= off_x * STBTT_FIX;++ z->ey = e->y1;+ z->next = 0;+ z->direction = e->invert ? 1 : -1;+ return z;+}+#elif STBTT_RASTERIZER_VERSION == 2+static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)+{+ stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);+ float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);+ STBTT_assert(z != NULL);+ //STBTT_assert(e->y0 <= start_point);+ if (!z) return z;+ z->fdx = dxdy;+ z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f;+ z->fx = e->x0 + dxdy * (start_point - e->y0);+ z->fx -= off_x;+ z->direction = e->invert ? 1.0f : -1.0f;+ z->sy = e->y0;+ z->ey = e->y1;+ z->next = 0;+ return z;+}+#else+#error "Unrecognized value of STBTT_RASTERIZER_VERSION"+#endif++#if STBTT_RASTERIZER_VERSION == 1+// note: this routine clips fills that extend off the edges... ideally this+// wouldn't happen, but it could happen if the truetype glyph bounding boxes+// are wrong, or if the user supplies a too-small bitmap+static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)+{+ // non-zero winding fill+ int x0=0, w=0;++ while (e) {+ if (w == 0) {+ // if we're currently at zero, we need to record the edge start point+ x0 = e->x; w += e->direction;+ } else {+ int x1 = e->x; w += e->direction;+ // if we went to zero, we need to draw+ if (w == 0) {+ int i = x0 >> STBTT_FIXSHIFT;+ int j = x1 >> STBTT_FIXSHIFT;++ if (i < len && j >= 0) {+ if (i == j) {+ // x0,x1 are the same pixel, so compute combined coverage+ scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT);+ } else {+ if (i >= 0) // add antialiasing for x0+ scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT);+ else+ i = -1; // clip++ if (j < len) // add antialiasing for x1+ scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT);+ else+ j = len; // clip++ for (++i; i < j; ++i) // fill pixels between x0 and x1+ scanline[i] = scanline[i] + (stbtt_uint8) max_weight;+ }+ }+ }+ }+ + e = e->next;+ }+}++static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)+{+ stbtt__hheap hh = { 0, 0, 0 };+ stbtt__active_edge *active = NULL;+ int y,j=0;+ int max_weight = (255 / vsubsample); // weight per vertical scanline+ int s; // vertical subsample index+ unsigned char scanline_data[512], *scanline;++ if (result->w > 512)+ scanline = (unsigned char *) STBTT_malloc(result->w, userdata);+ else+ scanline = scanline_data;++ y = off_y * vsubsample;+ e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;++ while (j < result->h) {+ STBTT_memset(scanline, 0, result->w);+ for (s=0; s < vsubsample; ++s) {+ // find center of pixel for this scanline+ float scan_y = y + 0.5f;+ stbtt__active_edge **step = &active;++ // update all active edges;+ // remove all active edges that terminate before the center of this scanline+ while (*step) {+ stbtt__active_edge * z = *step;+ if (z->ey <= scan_y) {+ *step = z->next; // delete from list+ STBTT_assert(z->direction);+ z->direction = 0;+ stbtt__hheap_free(&hh, z);+ } else {+ z->x += z->dx; // advance to position for current scanline+ step = &((*step)->next); // advance through list+ }+ }++ // resort the list if needed+ for(;;) {+ int changed=0;+ step = &active;+ while (*step && (*step)->next) {+ if ((*step)->x > (*step)->next->x) {+ stbtt__active_edge *t = *step;+ stbtt__active_edge *q = t->next;++ t->next = q->next;+ q->next = t;+ *step = q;+ changed = 1;+ }+ step = &(*step)->next;+ }+ if (!changed) break;+ }++ // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline+ while (e->y0 <= scan_y) {+ if (e->y1 > scan_y) {+ stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata);+ if (z != NULL) {+ // find insertion point+ if (active == NULL)+ active = z;+ else if (z->x < active->x) {+ // insert at front+ z->next = active;+ active = z;+ } else {+ // find thing to insert AFTER+ stbtt__active_edge *p = active;+ while (p->next && p->next->x < z->x)+ p = p->next;+ // at this point, p->next->x is NOT < z->x+ z->next = p->next;+ p->next = z;+ }+ }+ }+ ++e;+ }++ // now process all active edges in XOR fashion+ if (active)+ stbtt__fill_active_edges(scanline, result->w, active, max_weight);++ ++y;+ }+ STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);+ ++j;+ }++ stbtt__hheap_cleanup(&hh, userdata);++ if (scanline != scanline_data)+ STBTT_free(scanline, userdata);+}++#elif STBTT_RASTERIZER_VERSION == 2++// the edge passed in here does not cross the vertical line at x or the vertical line at x+1+// (i.e. it has already been clipped to those)+static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1)+{+ if (y0 == y1) return;+ STBTT_assert(y0 < y1);+ STBTT_assert(e->sy <= e->ey);+ if (y0 > e->ey) return;+ if (y1 < e->sy) return;+ if (y0 < e->sy) {+ x0 += (x1-x0) * (e->sy - y0) / (y1-y0);+ y0 = e->sy;+ }+ if (y1 > e->ey) {+ x1 += (x1-x0) * (e->ey - y1) / (y1-y0);+ y1 = e->ey;+ }++ if (x0 == x)+ STBTT_assert(x1 <= x+1);+ else if (x0 == x+1)+ STBTT_assert(x1 >= x);+ else if (x0 <= x)+ STBTT_assert(x1 <= x);+ else if (x0 >= x+1)+ STBTT_assert(x1 >= x+1);+ else+ STBTT_assert(x1 >= x && x1 <= x+1);++ if (x0 <= x && x1 <= x)+ scanline[x] += e->direction * (y1-y0);+ else if (x0 >= x+1 && x1 >= x+1)+ ;+ else {+ STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1);+ scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position+ }+}++static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)+{+ float y_bottom = y_top+1;++ while (e) {+ // brute force every pixel++ // compute intersection points with top & bottom+ STBTT_assert(e->ey >= y_top);++ if (e->fdx == 0) {+ float x0 = e->fx;+ if (x0 < len) {+ if (x0 >= 0) {+ stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom);+ stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom);+ } else {+ stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom);+ }+ }+ } else {+ float x0 = e->fx;+ float dx = e->fdx;+ float xb = x0 + dx;+ float x_top, x_bottom;+ float sy0,sy1;+ float dy = e->fdy;+ STBTT_assert(e->sy <= y_bottom && e->ey >= y_top);++ // compute endpoints of line segment clipped to this scanline (if the+ // line segment starts on this scanline. x0 is the intersection of the+ // line with y_top, but that may be off the line segment.+ if (e->sy > y_top) {+ x_top = x0 + dx * (e->sy - y_top);+ sy0 = e->sy;+ } else {+ x_top = x0;+ sy0 = y_top;+ }+ if (e->ey < y_bottom) {+ x_bottom = x0 + dx * (e->ey - y_top);+ sy1 = e->ey;+ } else {+ x_bottom = xb;+ sy1 = y_bottom;+ }++ if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) {+ // from here on, we don't have to range check x values++ if ((int) x_top == (int) x_bottom) {+ float height;+ // simple case, only spans one pixel+ int x = (int) x_top;+ height = sy1 - sy0;+ STBTT_assert(x >= 0 && x < len);+ scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height;+ scanline_fill[x] += e->direction * height; // everything right of this pixel is filled+ } else {+ int x,x1,x2;+ float y_crossing, step, sign, area;+ // covers 2+ pixels+ if (x_top > x_bottom) {+ // flip scanline vertically; signed area is the same+ float t;+ sy0 = y_bottom - (sy0 - y_top);+ sy1 = y_bottom - (sy1 - y_top);+ t = sy0, sy0 = sy1, sy1 = t;+ t = x_bottom, x_bottom = x_top, x_top = t;+ dx = -dx;+ dy = -dy;+ t = x0, x0 = xb, xb = t;+ }++ x1 = (int) x_top;+ x2 = (int) x_bottom;+ // compute intersection with y axis at x1+1+ y_crossing = (x1+1 - x0) * dy + y_top;++ sign = e->direction;+ // area of the rectangle covered from y0..y_crossing+ area = sign * (y_crossing-sy0);+ // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)+ scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2);++ step = sign * dy;+ for (x = x1+1; x < x2; ++x) {+ scanline[x] += area + step/2;+ area += step;+ }+ y_crossing += dy * (x2 - (x1+1));++ STBTT_assert(fabs(area) <= 1.01f);++ scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing);++ scanline_fill[x2] += sign * (sy1-sy0);+ }+ } else {+ // if edge goes outside of box we're drawing, we require+ // clipping logic. since this does not match the intended use+ // of this library, we use a different, very slow brute+ // force implementation+ int x;+ for (x=0; x < len; ++x) {+ // cases:+ //+ // there can be up to two intersections with the pixel. any intersection+ // with left or right edges can be handled by splitting into two (or three)+ // regions. intersections with top & bottom do not necessitate case-wise logic.+ //+ // the old way of doing this found the intersections with the left & right edges,+ // then used some simple logic to produce up to three segments in sorted order+ // from top-to-bottom. however, this had a problem: if an x edge was epsilon+ // across the x border, then the corresponding y position might not be distinct+ // from the other y segment, and it might ignored as an empty segment. to avoid+ // that, we need to explicitly produce segments based on x positions.++ // rename variables to clear pairs+ float y0 = y_top;+ float x1 = (float) (x);+ float x2 = (float) (x+1);+ float x3 = xb;+ float y3 = y_bottom;+ float y1,y2;++ // x = e->x + e->dx * (y-y_top)+ // (y-y_top) = (x - e->x) / e->dx+ // y = (x - e->x) / e->dx + y_top+ y1 = (x - x0) / dx + y_top;+ y2 = (x+1 - x0) / dx + y_top;++ if (x0 < x1 && x3 > x2) { // three segments descending down-right+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);+ stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2);+ stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);+ } else if (x3 < x1 && x0 > x2) { // three segments descending down-left+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);+ stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1);+ stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);+ } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);+ stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);+ } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);+ stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);+ } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);+ stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);+ } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);+ stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);+ } else { // one segment+ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3);+ }+ }+ }+ }+ e = e->next;+ }+}++// directly AA rasterize edges w/o supersampling+static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)+{+ stbtt__hheap hh = { 0, 0, 0 };+ stbtt__active_edge *active = NULL;+ int y,j=0, i;+ float scanline_data[129], *scanline, *scanline2;++ if (result->w > 64)+ scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata);+ else+ scanline = scanline_data;++ scanline2 = scanline + result->w;++ y = off_y;+ e[n].y0 = (float) (off_y + result->h) + 1;++ while (j < result->h) {+ // find center of pixel for this scanline+ float scan_y_top = y + 0.0f;+ float scan_y_bottom = y + 1.0f;+ stbtt__active_edge **step = &active;++ STBTT_memset(scanline , 0, result->w*sizeof(scanline[0]));+ STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0]));++ // update all active edges;+ // remove all active edges that terminate before the top of this scanline+ while (*step) {+ stbtt__active_edge * z = *step;+ if (z->ey <= scan_y_top) {+ *step = z->next; // delete from list+ STBTT_assert(z->direction);+ z->direction = 0;+ stbtt__hheap_free(&hh, z);+ } else {+ step = &((*step)->next); // advance through list+ }+ }++ // insert all edges that start before the bottom of this scanline+ while (e->y0 <= scan_y_bottom) {+ if (e->y0 != e->y1) {+ stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata);+ if (z != NULL) {+ STBTT_assert(z->ey >= scan_y_top);+ // insert at front+ z->next = active;+ active = z;+ }+ }+ ++e;+ }++ // now process all active edges+ if (active)+ stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top);++ {+ float sum = 0;+ for (i=0; i < result->w; ++i) {+ float k;+ int m;+ sum += scanline2[i];+ k = scanline[i] + sum;+ k = (float) fabs(k)*255 + 0.5f;+ m = (int) k;+ if (m > 255) m = 255;+ result->pixels[j*result->stride + i] = (unsigned char) m;+ }+ }+ // advance all the edges+ step = &active;+ while (*step) {+ stbtt__active_edge *z = *step;+ z->fx += z->fdx; // advance to position for current scanline+ step = &((*step)->next); // advance through list+ }++ ++y;+ ++j;+ }++ stbtt__hheap_cleanup(&hh, userdata);++ if (scanline != scanline_data)+ STBTT_free(scanline, userdata);+}+#else+#error "Unrecognized value of STBTT_RASTERIZER_VERSION"+#endif++#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0)++static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n)+{+ int i,j;+ for (i=1; i < n; ++i) {+ stbtt__edge t = p[i], *a = &t;+ j = i;+ while (j > 0) {+ stbtt__edge *b = &p[j-1];+ int c = STBTT__COMPARE(a,b);+ if (!c) break;+ p[j] = p[j-1];+ --j;+ }+ if (i != j)+ p[j] = t;+ }+}++static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n)+{+ /* threshhold for transitioning to insertion sort */+ while (n > 12) {+ stbtt__edge t;+ int c01,c12,c,m,i,j;++ /* compute median of three */+ m = n >> 1;+ c01 = STBTT__COMPARE(&p[0],&p[m]);+ c12 = STBTT__COMPARE(&p[m],&p[n-1]);+ /* if 0 >= mid >= end, or 0 < mid < end, then use mid */+ if (c01 != c12) {+ /* otherwise, we'll need to swap something else to middle */+ int z;+ c = STBTT__COMPARE(&p[0],&p[n-1]);+ /* 0>mid && mid<n: 0>n => n; 0<n => 0 */+ /* 0<mid && mid>n: 0>n => 0; 0<n => n */+ z = (c == c12) ? 0 : n-1;+ t = p[z];+ p[z] = p[m];+ p[m] = t;+ }+ /* now p[m] is the median-of-three */+ /* swap it to the beginning so it won't move around */+ t = p[0];+ p[0] = p[m];+ p[m] = t;++ /* partition loop */+ i=1;+ j=n-1;+ for(;;) {+ /* handling of equality is crucial here */+ /* for sentinels & efficiency with duplicates */+ for (;;++i) {+ if (!STBTT__COMPARE(&p[i], &p[0])) break;+ }+ for (;;--j) {+ if (!STBTT__COMPARE(&p[0], &p[j])) break;+ }+ /* make sure we haven't crossed */+ if (i >= j) break;+ t = p[i];+ p[i] = p[j];+ p[j] = t;++ ++i;+ --j;+ }+ /* recurse on smaller side, iterate on larger */+ if (j < (n-i)) {+ stbtt__sort_edges_quicksort(p,j);+ p = p+i;+ n = n-i;+ } else {+ stbtt__sort_edges_quicksort(p+i, n-i);+ n = j;+ }+ }+}++static void stbtt__sort_edges(stbtt__edge *p, int n)+{+ stbtt__sort_edges_quicksort(p, n);+ stbtt__sort_edges_ins_sort(p, n);+}++typedef struct+{+ float x,y;+} stbtt__point;++static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata)+{+ float y_scale_inv = invert ? -scale_y : scale_y;+ stbtt__edge *e;+ int n,i,j,k,m;+#if STBTT_RASTERIZER_VERSION == 1+ int vsubsample = result->h < 8 ? 15 : 5;+#elif STBTT_RASTERIZER_VERSION == 2+ int vsubsample = 1;+#else+ #error "Unrecognized value of STBTT_RASTERIZER_VERSION"+#endif+ // vsubsample should divide 255 evenly; otherwise we won't reach full opacity++ // now we have to blow out the windings into explicit edge lists+ n = 0;+ for (i=0; i < windings; ++i)+ n += wcount[i];++ e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel+ if (e == 0) return;+ n = 0;++ m=0;+ for (i=0; i < windings; ++i) {+ stbtt__point *p = pts + m;+ m += wcount[i];+ j = wcount[i]-1;+ for (k=0; k < wcount[i]; j=k++) {+ int a=k,b=j;+ // skip the edge if horizontal+ if (p[j].y == p[k].y)+ continue;+ // add edge from j to k to the list+ e[n].invert = 0;+ if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {+ e[n].invert = 1;+ a=j,b=k;+ }+ e[n].x0 = p[a].x * scale_x + shift_x;+ e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample;+ e[n].x1 = p[b].x * scale_x + shift_x;+ e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample;+ ++n;+ }+ }++ // now sort the edges by their highest point (should snap to integer, and then by x)+ //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);+ stbtt__sort_edges(e, n);++ // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule+ stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);++ STBTT_free(e, userdata);+}++static void stbtt__add_point(stbtt__point *points, int n, float x, float y)+{+ if (!points) return; // during first pass, it's unallocated+ points[n].x = x;+ points[n].y = y;+}++// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching+static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)+{+ // midpoint+ float mx = (x0 + 2*x1 + x2)/4;+ float my = (y0 + 2*y1 + y2)/4;+ // versus directly drawn line+ float dx = (x0+x2)/2 - mx;+ float dy = (y0+y2)/2 - my;+ if (n > 16) // 65536 segments on one curve better be enough!+ return 1;+ if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA+ stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);+ stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);+ } else {+ stbtt__add_point(points, *num_points,x2,y2);+ *num_points = *num_points+1;+ }+ return 1;+}++// returns number of contours+static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)+{+ stbtt__point *points=0;+ int num_points=0;++ float objspace_flatness_squared = objspace_flatness * objspace_flatness;+ int i,n=0,start=0, pass;++ // count how many "moves" there are to get the contour count+ for (i=0; i < num_verts; ++i)+ if (vertices[i].type == STBTT_vmove)+ ++n;++ *num_contours = n;+ if (n == 0) return 0;++ *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);++ if (*contour_lengths == 0) {+ *num_contours = 0;+ return 0;+ }++ // make two passes through the points so we don't need to realloc+ for (pass=0; pass < 2; ++pass) {+ float x=0,y=0;+ if (pass == 1) {+ points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);+ if (points == NULL) goto error;+ }+ num_points = 0;+ n= -1;+ for (i=0; i < num_verts; ++i) {+ switch (vertices[i].type) {+ case STBTT_vmove:+ // start the next contour+ if (n >= 0)+ (*contour_lengths)[n] = num_points - start;+ ++n;+ start = num_points;++ x = vertices[i].x, y = vertices[i].y;+ stbtt__add_point(points, num_points++, x,y);+ break;+ case STBTT_vline:+ x = vertices[i].x, y = vertices[i].y;+ stbtt__add_point(points, num_points++, x, y);+ break;+ case STBTT_vcurve:+ stbtt__tesselate_curve(points, &num_points, x,y,+ vertices[i].cx, vertices[i].cy,+ vertices[i].x, vertices[i].y,+ objspace_flatness_squared, 0);+ x = vertices[i].x, y = vertices[i].y;+ break;+ }+ }+ (*contour_lengths)[n] = num_points - start;+ }++ return points;+error:+ STBTT_free(points, userdata);+ STBTT_free(*contour_lengths, userdata);+ *contour_lengths = 0;+ *num_contours = 0;+ return NULL;+}++STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)+{+ float scale = scale_x > scale_y ? scale_y : scale_x;+ int winding_count, *winding_lengths;+ stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);+ if (windings) {+ stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata);+ STBTT_free(winding_lengths, userdata);+ STBTT_free(windings, userdata);+ }+}++STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)+{+ STBTT_free(bitmap, userdata);+}++STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)+{+ int ix0,iy0,ix1,iy1;+ stbtt__bitmap gbm;+ stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);++ if (scale_x == 0) scale_x = scale_y;+ if (scale_y == 0) {+ if (scale_x == 0) return NULL;+ scale_y = scale_x;+ }++ stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1);++ // now we get the size+ gbm.w = (ix1 - ix0);+ gbm.h = (iy1 - iy0);+ gbm.pixels = NULL; // in case we error++ if (width ) *width = gbm.w;+ if (height) *height = gbm.h;+ if (xoff ) *xoff = ix0;+ if (yoff ) *yoff = iy0;+ + if (gbm.w && gbm.h) {+ gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);+ if (gbm.pixels) {+ gbm.stride = gbm.w;++ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata);+ }+ }+ STBTT_free(vertices, info->userdata);+ return gbm.pixels;+} ++STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)+{+ return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff);+}++STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)+{+ int ix0,iy0;+ stbtt_vertex *vertices;+ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);+ stbtt__bitmap gbm; ++ stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);+ gbm.pixels = output;+ gbm.w = out_w;+ gbm.h = out_h;+ gbm.stride = out_stride;++ if (gbm.w && gbm.h)+ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata);++ STBTT_free(vertices, info->userdata);+}++STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)+{+ stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph);+}++STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)+{+ return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);+} ++STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)+{+ stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint));+}++STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)+{+ return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);+} ++STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)+{+ stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint);+}++//////////////////////////////////////////////////////////////////////////////+//+// bitmap baking+//+// This is SUPER-CRAPPY packing to keep source code small++STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)+ float pixel_height, // height of font in pixels+ unsigned char *pixels, int pw, int ph, // bitmap to be filled in+ int first_char, int num_chars, // characters to bake+ stbtt_bakedchar *chardata)+{+ float scale;+ int x,y,bottom_y, i;+ stbtt_fontinfo f;+ f.userdata = NULL;+ if (!stbtt_InitFont(&f, data, offset))+ return -1;+ STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels+ x=y=1;+ bottom_y = 1;++ scale = stbtt_ScaleForPixelHeight(&f, pixel_height);++ for (i=0; i < num_chars; ++i) {+ int advance, lsb, x0,y0,x1,y1,gw,gh;+ int g = stbtt_FindGlyphIndex(&f, first_char + i);+ stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);+ stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);+ gw = x1-x0;+ gh = y1-y0;+ if (x + gw + 1 >= pw)+ y = bottom_y, x = 1; // advance to next row+ if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row+ return -i;+ STBTT_assert(x+gw < pw);+ STBTT_assert(y+gh < ph);+ stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);+ chardata[i].x0 = (stbtt_int16) x;+ chardata[i].y0 = (stbtt_int16) y;+ chardata[i].x1 = (stbtt_int16) (x + gw);+ chardata[i].y1 = (stbtt_int16) (y + gh);+ chardata[i].xadvance = scale * advance;+ chardata[i].xoff = (float) x0;+ chardata[i].yoff = (float) y0;+ x = x + gw + 1;+ if (y+gh+1 > bottom_y)+ bottom_y = y+gh+1;+ }+ return bottom_y;+}++STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)+{+ float d3d_bias = opengl_fillrule ? 0 : -0.5f;+ float ipw = 1.0f / pw, iph = 1.0f / ph;+ stbtt_bakedchar *b = chardata + char_index;+ int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f);+ int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f);++ q->x0 = round_x + d3d_bias;+ q->y0 = round_y + d3d_bias;+ q->x1 = round_x + b->x1 - b->x0 + d3d_bias;+ q->y1 = round_y + b->y1 - b->y0 + d3d_bias;++ q->s0 = b->x0 * ipw;+ q->t0 = b->y0 * iph;+ q->s1 = b->x1 * ipw;+ q->t1 = b->y1 * iph;++ *xpos += b->xadvance;+}++//////////////////////////////////////////////////////////////////////////////+//+// rectangle packing replacement routines if you don't have stb_rect_pack.h+//++#ifndef STB_RECT_PACK_VERSION+#ifdef _MSC_VER+#define STBTT__NOTUSED(v) (void)(v)+#else+#define STBTT__NOTUSED(v) (void)sizeof(v)+#endif++typedef int stbrp_coord;++////////////////////////////////////////////////////////////////////////////////////+// //+// //+// COMPILER WARNING ?!?!? //+// //+// //+// if you get a compile warning due to these symbols being defined more than //+// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" //+// //+////////////////////////////////////////////////////////////////////////////////////++typedef struct+{+ int width,height;+ int x,y,bottom_y;+} stbrp_context;++typedef struct+{+ unsigned char x;+} stbrp_node;++struct stbrp_rect+{+ stbrp_coord x,y;+ int id,w,h,was_packed;+};++static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes)+{+ con->width = pw;+ con->height = ph;+ con->x = 0;+ con->y = 0;+ con->bottom_y = 0;+ STBTT__NOTUSED(nodes);+ STBTT__NOTUSED(num_nodes); +}++static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)+{+ int i;+ for (i=0; i < num_rects; ++i) {+ if (con->x + rects[i].w > con->width) {+ con->x = 0;+ con->y = con->bottom_y;+ }+ if (con->y + rects[i].h > con->height)+ break;+ rects[i].x = con->x;+ rects[i].y = con->y;+ rects[i].was_packed = 1;+ con->x += rects[i].w;+ if (con->y + rects[i].h > con->bottom_y)+ con->bottom_y = con->y + rects[i].h;+ }+ for ( ; i < num_rects; ++i)+ rects[i].was_packed = 0;+}+#endif++//////////////////////////////////////////////////////////////////////////////+//+// bitmap baking+//+// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If+// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy.++STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)+{+ stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context);+ int num_nodes = pw - padding;+ stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context);++ if (context == NULL || nodes == NULL) {+ if (context != NULL) STBTT_free(context, alloc_context);+ if (nodes != NULL) STBTT_free(nodes , alloc_context);+ return 0;+ }++ spc->user_allocator_context = alloc_context;+ spc->width = pw;+ spc->height = ph;+ spc->pixels = pixels;+ spc->pack_info = context;+ spc->nodes = nodes;+ spc->padding = padding;+ spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;+ spc->h_oversample = 1;+ spc->v_oversample = 1;++ stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);++ if (pixels)+ STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels++ return 1;+}++STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc)+{+ STBTT_free(spc->nodes , spc->user_allocator_context);+ STBTT_free(spc->pack_info, spc->user_allocator_context);+}++STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)+{+ STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE);+ STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE);+ if (h_oversample <= STBTT_MAX_OVERSAMPLE)+ spc->h_oversample = h_oversample;+ if (v_oversample <= STBTT_MAX_OVERSAMPLE)+ spc->v_oversample = v_oversample;+}++#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1)++static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)+{+ unsigned char buffer[STBTT_MAX_OVERSAMPLE];+ int safe_w = w - kernel_width;+ int j;+ STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze+ for (j=0; j < h; ++j) {+ int i;+ unsigned int total;+ STBTT_memset(buffer, 0, kernel_width);++ total = 0;++ // make kernel_width a constant in common cases so compiler can optimize out the divide+ switch (kernel_width) {+ case 2:+ for (i=0; i <= safe_w; ++i) {+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];+ pixels[i] = (unsigned char) (total / 2);+ }+ break;+ case 3:+ for (i=0; i <= safe_w; ++i) {+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];+ pixels[i] = (unsigned char) (total / 3);+ }+ break;+ case 4:+ for (i=0; i <= safe_w; ++i) {+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];+ pixels[i] = (unsigned char) (total / 4);+ }+ break;+ case 5:+ for (i=0; i <= safe_w; ++i) {+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];+ pixels[i] = (unsigned char) (total / 5);+ }+ break;+ default:+ for (i=0; i <= safe_w; ++i) {+ total += pixels[i] - buffer[i & STBTT__OVER_MASK];+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];+ pixels[i] = (unsigned char) (total / kernel_width);+ }+ break;+ }++ for (; i < w; ++i) {+ STBTT_assert(pixels[i] == 0);+ total -= buffer[i & STBTT__OVER_MASK];+ pixels[i] = (unsigned char) (total / kernel_width);+ }++ pixels += stride_in_bytes;+ }+}++static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)+{+ unsigned char buffer[STBTT_MAX_OVERSAMPLE];+ int safe_h = h - kernel_width;+ int j;+ STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze+ for (j=0; j < w; ++j) {+ int i;+ unsigned int total;+ STBTT_memset(buffer, 0, kernel_width);++ total = 0;++ // make kernel_width a constant in common cases so compiler can optimize out the divide+ switch (kernel_width) {+ case 2:+ for (i=0; i <= safe_h; ++i) {+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];+ pixels[i*stride_in_bytes] = (unsigned char) (total / 2);+ }+ break;+ case 3:+ for (i=0; i <= safe_h; ++i) {+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];+ pixels[i*stride_in_bytes] = (unsigned char) (total / 3);+ }+ break;+ case 4:+ for (i=0; i <= safe_h; ++i) {+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];+ pixels[i*stride_in_bytes] = (unsigned char) (total / 4);+ }+ break;+ case 5:+ for (i=0; i <= safe_h; ++i) {+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];+ pixels[i*stride_in_bytes] = (unsigned char) (total / 5);+ }+ break;+ default:+ for (i=0; i <= safe_h; ++i) {+ total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];+ buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];+ pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);+ }+ break;+ }++ for (; i < h; ++i) {+ STBTT_assert(pixels[i*stride_in_bytes] == 0);+ total -= buffer[i & STBTT__OVER_MASK];+ pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);+ }++ pixels += 1;+ }+}++static float stbtt__oversample_shift(int oversample)+{+ if (!oversample)+ return 0.0f;++ // The prefilter is a box filter of width "oversample",+ // which shifts phase by (oversample - 1)/2 pixels in+ // oversampled space. We want to shift in the opposite+ // direction to counter this.+ return (float)-(oversample - 1) / (2.0f * (float)oversample);+}++// rects array must be big enough to accommodate all characters in the given ranges+STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)+{+ int i,j,k;++ k=0;+ for (i=0; i < num_ranges; ++i) {+ float fh = ranges[i].font_size;+ float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);+ ranges[i].h_oversample = (unsigned char) spc->h_oversample;+ ranges[i].v_oversample = (unsigned char) spc->v_oversample;+ for (j=0; j < ranges[i].num_chars; ++j) {+ int x0,y0,x1,y1;+ int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];+ int glyph = stbtt_FindGlyphIndex(info, codepoint);+ stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,+ scale * spc->h_oversample,+ scale * spc->v_oversample,+ 0,0,+ &x0,&y0,&x1,&y1);+ rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);+ rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);+ ++k;+ }+ }++ return k;+}++// rects array must be big enough to accommodate all characters in the given ranges+STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)+{+ int i,j,k, return_value = 1;++ // save current values+ int old_h_over = spc->h_oversample;+ int old_v_over = spc->v_oversample;++ k = 0;+ for (i=0; i < num_ranges; ++i) {+ float fh = ranges[i].font_size;+ float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);+ float recip_h,recip_v,sub_x,sub_y;+ spc->h_oversample = ranges[i].h_oversample;+ spc->v_oversample = ranges[i].v_oversample;+ recip_h = 1.0f / spc->h_oversample;+ recip_v = 1.0f / spc->v_oversample;+ sub_x = stbtt__oversample_shift(spc->h_oversample);+ sub_y = stbtt__oversample_shift(spc->v_oversample);+ for (j=0; j < ranges[i].num_chars; ++j) {+ stbrp_rect *r = &rects[k];+ if (r->was_packed) {+ stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];+ int advance, lsb, x0,y0,x1,y1;+ int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];+ int glyph = stbtt_FindGlyphIndex(info, codepoint);+ stbrp_coord pad = (stbrp_coord) spc->padding;++ // pad on left and top+ r->x += pad;+ r->y += pad;+ r->w -= pad;+ r->h -= pad;+ stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb);+ stbtt_GetGlyphBitmapBox(info, glyph,+ scale * spc->h_oversample,+ scale * spc->v_oversample,+ &x0,&y0,&x1,&y1);+ stbtt_MakeGlyphBitmapSubpixel(info,+ spc->pixels + r->x + r->y*spc->stride_in_bytes,+ r->w - spc->h_oversample+1,+ r->h - spc->v_oversample+1,+ spc->stride_in_bytes,+ scale * spc->h_oversample,+ scale * spc->v_oversample,+ 0,0,+ glyph);++ if (spc->h_oversample > 1)+ stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,+ r->w, r->h, spc->stride_in_bytes,+ spc->h_oversample);++ if (spc->v_oversample > 1)+ stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,+ r->w, r->h, spc->stride_in_bytes,+ spc->v_oversample);++ bc->x0 = (stbtt_int16) r->x;+ bc->y0 = (stbtt_int16) r->y;+ bc->x1 = (stbtt_int16) (r->x + r->w);+ bc->y1 = (stbtt_int16) (r->y + r->h);+ bc->xadvance = scale * advance;+ bc->xoff = (float) x0 * recip_h + sub_x;+ bc->yoff = (float) y0 * recip_v + sub_y;+ bc->xoff2 = (x0 + r->w) * recip_h + sub_x;+ bc->yoff2 = (y0 + r->h) * recip_v + sub_y;+ } else {+ return_value = 0; // if any fail, report failure+ }++ ++k;+ }+ }++ // restore original values+ spc->h_oversample = old_h_over;+ spc->v_oversample = old_v_over;++ return return_value;+}++STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects)+{+ stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects);+}++STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)+{+ stbtt_fontinfo info;+ int i,j,n, return_value = 1;+ //stbrp_context *context = (stbrp_context *) spc->pack_info;+ stbrp_rect *rects;++ // flag all characters as NOT packed+ for (i=0; i < num_ranges; ++i)+ for (j=0; j < ranges[i].num_chars; ++j)+ ranges[i].chardata_for_range[j].x0 =+ ranges[i].chardata_for_range[j].y0 =+ ranges[i].chardata_for_range[j].x1 =+ ranges[i].chardata_for_range[j].y1 = 0;++ n = 0;+ for (i=0; i < num_ranges; ++i)+ n += ranges[i].num_chars;+ + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);+ if (rects == NULL)+ return 0;++ info.userdata = spc->user_allocator_context;+ stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));++ n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);++ stbtt_PackFontRangesPackRects(spc, rects, n);+ + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);++ STBTT_free(rects, spc->user_allocator_context);+ return return_value;+}++STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,+ int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)+{+ stbtt_pack_range range;+ range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range;+ range.array_of_unicode_codepoints = NULL;+ range.num_chars = num_chars_in_range;+ range.chardata_for_range = chardata_for_range;+ range.font_size = font_size;+ return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);+}++STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)+{+ float ipw = 1.0f / pw, iph = 1.0f / ph;+ stbtt_packedchar *b = chardata + char_index;++ if (align_to_integer) {+ float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);+ float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f);+ q->x0 = x;+ q->y0 = y;+ q->x1 = x + b->xoff2 - b->xoff;+ q->y1 = y + b->yoff2 - b->yoff;+ } else {+ q->x0 = *xpos + b->xoff;+ q->y0 = *ypos + b->yoff;+ q->x1 = *xpos + b->xoff2;+ q->y1 = *ypos + b->yoff2;+ }++ q->s0 = b->x0 * ipw;+ q->t0 = b->y0 * iph;+ q->s1 = b->x1 * ipw;+ q->t1 = b->y1 * iph;++ *xpos += b->xadvance;+}+++//////////////////////////////////////////////////////////////////////////////+//+// font name matching -- recommended not to use this+//++// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string+static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) +{+ stbtt_int32 i=0;++ // convert utf16 to utf8 and compare the results while converting+ while (len2) {+ stbtt_uint16 ch = s2[0]*256 + s2[1];+ if (ch < 0x80) {+ if (i >= len1) return -1;+ if (s1[i++] != ch) return -1;+ } else if (ch < 0x800) {+ if (i+1 >= len1) return -1;+ if (s1[i++] != 0xc0 + (ch >> 6)) return -1;+ if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;+ } else if (ch >= 0xd800 && ch < 0xdc00) {+ stbtt_uint32 c;+ stbtt_uint16 ch2 = s2[2]*256 + s2[3];+ if (i+3 >= len1) return -1;+ c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;+ if (s1[i++] != 0xf0 + (c >> 18)) return -1;+ if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;+ if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1;+ if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1;+ s2 += 2; // plus another 2 below+ len2 -= 2;+ } else if (ch >= 0xdc00 && ch < 0xe000) {+ return -1;+ } else {+ if (i+2 >= len1) return -1;+ if (s1[i++] != 0xe0 + (ch >> 12)) return -1;+ if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;+ if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1;+ }+ s2 += 2;+ len2 -= 2;+ }+ return i;+}++STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{+ return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2);+}++// returns results in whatever encoding you request... but note that 2-byte encodings+// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare+STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)+{+ stbtt_int32 i,count,stringOffset;+ stbtt_uint8 *fc = font->data;+ stbtt_uint32 offset = font->fontstart;+ stbtt_uint32 nm = stbtt__find_table(fc, offset, "name");+ if (!nm) return NULL;++ count = ttUSHORT(fc+nm+2);+ stringOffset = nm + ttUSHORT(fc+nm+4);+ for (i=0; i < count; ++i) {+ stbtt_uint32 loc = nm + 6 + 12 * i;+ if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)+ && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {+ *length = ttUSHORT(fc+loc+8);+ return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10));+ }+ }+ return NULL;+}++static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)+{+ stbtt_int32 i;+ stbtt_int32 count = ttUSHORT(fc+nm+2);+ stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);++ for (i=0; i < count; ++i) {+ stbtt_uint32 loc = nm + 6 + 12 * i;+ stbtt_int32 id = ttUSHORT(fc+loc+6);+ if (id == target_id) {+ // find the encoding+ stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);++ // is this a Unicode encoding?+ if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {+ stbtt_int32 slen = ttUSHORT(fc+loc+8);+ stbtt_int32 off = ttUSHORT(fc+loc+10);++ // check if there's a prefix match+ stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);+ if (matchlen >= 0) {+ // check for target_id+1 immediately following, with same encoding & language+ if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {+ slen = ttUSHORT(fc+loc+12+8);+ off = ttUSHORT(fc+loc+12+10);+ if (slen == 0) {+ if (matchlen == nlen)+ return 1;+ } else if (matchlen < nlen && name[matchlen] == ' ') {+ ++matchlen;+ if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))+ return 1;+ }+ } else {+ // if nothing immediately following+ if (matchlen == nlen)+ return 1;+ }+ }+ }++ // @TODO handle other encodings+ }+ }+ return 0;+}++static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)+{+ stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name);+ stbtt_uint32 nm,hd;+ if (!stbtt__isfont(fc+offset)) return 0;++ // check italics/bold/underline flags in macStyle...+ if (flags) {+ hd = stbtt__find_table(fc, offset, "head");+ if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;+ }++ nm = stbtt__find_table(fc, offset, "name");+ if (!nm) return 0;++ if (flags) {+ // if we checked the macStyle flags, then just check the family and ignore the subfamily+ if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1;+ if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1;+ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;+ } else {+ if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1;+ if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1;+ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;+ }++ return 0;+}++STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags)+{+ stbtt_int32 i;+ for (i=0;;++i) {+ stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);+ if (off < 0) return off;+ if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))+ return off;+ }+}++#endif // STB_TRUETYPE_IMPLEMENTATION+++// FULL VERSION HISTORY+//+// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges+// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges+// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;+// allow PackFontRanges to pack and render in separate phases;+// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);+// fixed an assert() bug in the new rasterizer+// replace assert() with STBTT_assert() in new rasterizer+// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)+// also more precise AA rasterizer, except if shapes overlap+// remove need for STBTT_sort+// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC+// 1.04 (2015-04-15) typo in example+// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes+// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C+++// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match+// non-oversampled; STBTT_POINT_SIZE for packed case only+// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling+// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg)+// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID+// 0.8b (2014-07-07) fix a warning+// 0.8 (2014-05-25) fix a few more warnings+// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back+// 0.6c (2012-07-24) improve documentation+// 0.6b (2012-07-20) fix a few more warnings+// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels,+// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty+// 0.5 (2011-12-09) bugfixes:+// subpixel glyph renderer computed wrong bounding box+// first vertex of shape can be off-curve (FreeSans)+// 0.4b (2011-12-03) fixed an error in the font baking example+// 0.4 (2011-12-01) kerning, subpixel rendering (tor)+// bugfixes for:+// codepoint-to-glyph conversion using table fmt=12+// codepoint-to-glyph conversion using table fmt=4+// stbtt_GetBakedQuad with non-square texture (Zer)+// updated Hello World! sample to use kerning and subpixel+// fixed some warnings+// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM)+// userdata, malloc-from-userdata, non-zero fill (stb)+// 0.2 (2009-03-11) Fix unsigned/signed char warnings+// 0.1 (2009-03-09) First public release+//