From da52b2f675c9ad9b161023b1b5d3c45a5079a087 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sat, 30 Oct 2021 16:34:56 -0500 Subject: [PATCH] Textbox "finished". Widget Del methods made private. stb_leakcheck replaced with MemWatch. --- LICENSE | 8 +- client/client.pro | 11 +- client/src/gui/array.h | 1 + client/src/gui/button.c | 3 +- client/src/gui/button.h | 1 - client/src/gui/checkbox.c | 3 +- client/src/gui/checkbox.h | 1 - client/src/gui/desktop.c | 3 +- client/src/gui/desktop.h | 1 - client/src/gui/frame.c | 3 +- client/src/gui/frame.h | 1 - client/src/gui/gui.c | 5 +- client/src/gui/label.c | 3 +- client/src/gui/label.h | 1 - client/src/gui/memory.c | 22 +- client/src/gui/memory.h | 9 +- client/src/gui/os.h | 12 +- client/src/gui/picture.c | 3 +- client/src/gui/picture.h | 1 - client/src/gui/radio.c | 3 +- client/src/gui/radio.h | 1 - client/src/gui/textbox.c | 30 +- client/src/gui/textbox.h | 1 - client/src/gui/widget.c | 13 +- client/src/gui/window.c | 3 +- client/src/gui/window.h | 1 - client/src/main.c | 10 +- client/src/thirdparty/memwatch/FAQ | 133 + client/src/thirdparty/memwatch/Makefile | 2 + client/src/thirdparty/memwatch/README | 99 + client/src/thirdparty/memwatch/USING | 213 + client/src/thirdparty/memwatch/gpl.txt | 340 + client/src/thirdparty/memwatch/memwatch.c | 2664 ++++++++ client/src/thirdparty/memwatch/memwatch.h | 707 ++ client/src/thirdparty/memwatch/memwatch.lsm | 15 + client/src/thirdparty/memwatch/test.c | 116 + client/src/thirdparty/stb_image.h | 6414 +++++++++---------- client/src/thirdparty/stb_leakcheck.h | 194 - 38 files changed, 7601 insertions(+), 3450 deletions(-) create mode 100644 client/src/thirdparty/memwatch/FAQ create mode 100644 client/src/thirdparty/memwatch/Makefile create mode 100644 client/src/thirdparty/memwatch/README create mode 100644 client/src/thirdparty/memwatch/USING create mode 100644 client/src/thirdparty/memwatch/gpl.txt create mode 100644 client/src/thirdparty/memwatch/memwatch.c create mode 100644 client/src/thirdparty/memwatch/memwatch.h create mode 100644 client/src/thirdparty/memwatch/memwatch.lsm create mode 100644 client/src/thirdparty/memwatch/test.c delete mode 100644 client/src/thirdparty/stb_leakcheck.h diff --git a/LICENSE b/LICENSE index 4672049..f6a37fa 100644 --- a/LICENSE +++ b/LICENSE @@ -22,11 +22,11 @@ Licenses Used By: Client ====== -stb_ds.h -https://github.com/nothings/stb -Public Domain +MemWatch +http://www.linkdata.se/sourcecode/memwatch/ +GPL2 -stb_leakcheck.h +stb_ds.h https://github.com/nothings/stb Public Domain diff --git a/client/client.pro b/client/client.pro index e202457..acc559f 100644 --- a/client/client.pro +++ b/client/client.pro @@ -44,6 +44,10 @@ INCLUDEPATH += \ HEADERS = \ $$LINUX_HEADERS \ + src/thirdparty/stb_ds.h \ + src/thirdparty/stb_image.h \ + src/thirdparty/memwatch/memwatch.h \ + src/gui/memory.h \ src/gui/button.h \ src/gui/checkbox.h \ src/gui/frame.h \ @@ -55,9 +59,6 @@ HEADERS = \ src/gui/task.h \ src/gui/textbox.h \ src/gui/timer.h \ - src/thirdparty/stb_ds.h \ - src/thirdparty/stb_leakcheck.h \ - src/thirdparty/stb_image.h \ src/gui/array.h \ src/gui/font.h \ src/gui/desktop.h \ @@ -65,7 +66,6 @@ HEADERS = \ src/gui/widget.h \ src/gui/window.h \ src/gui/log.h \ - src/gui/memory.h \ src/gui/mouse.h \ src/gui/vesa.h \ src/gui/image.h \ @@ -73,11 +73,12 @@ HEADERS = \ SOURCES = \ $$LINUX_SOURCES \ + src/thirdparty/memwatch/memwatch.c \ + src/gui/memory.c \ src/gui/array.c \ src/gui/font.c \ src/gui/image.c \ src/gui/log.c \ - src/gui/memory.c \ src/gui/timer.c \ src/gui/task.c \ src/gui/gui.c \ diff --git a/client/src/gui/array.h b/client/src/gui/array.h index e8a1c73..b155fc1 100644 --- a/client/src/gui/array.h +++ b/client/src/gui/array.h @@ -22,6 +22,7 @@ #define ARRAY_H +#include "os.h" #include "stb_ds.h" diff --git a/client/src/gui/button.c b/client/src/gui/button.c index c877a46..a844099 100644 --- a/client/src/gui/button.c +++ b/client/src/gui/button.c @@ -21,11 +21,12 @@ #include "button.h" +static void buttonDel(WidgetT **widget); static void buttonMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event); static void buttonPaint(WidgetT *widget, RectT pos); -void buttonDel(WidgetT **widget) { +static void buttonDel(WidgetT **widget) { ButtonT *b = (ButtonT *)*widget; if (b->title) free(b->title); diff --git a/client/src/gui/button.h b/client/src/gui/button.h index 784427f..b1fffa1 100644 --- a/client/src/gui/button.h +++ b/client/src/gui/button.h @@ -33,7 +33,6 @@ typedef struct ButtonS { } ButtonT; -void buttonDel(WidgetT **widget); WidgetT *buttonInit(WidgetT *button, char *title, widgetCallback callback); ButtonT *buttonNew(uint16_t x, uint16_t y, char *title, widgetCallback callback); void buttonSetClickHandler(ButtonT *button, widgetCallback callback); diff --git a/client/src/gui/checkbox.c b/client/src/gui/checkbox.c index 02a9610..7d3408f 100644 --- a/client/src/gui/checkbox.c +++ b/client/src/gui/checkbox.c @@ -21,11 +21,12 @@ #include "checkbox.h" +static void checkboxDel(WidgetT **widget); static void checkboxMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event); static void checkboxPaint(WidgetT *widget, RectT pos); -void checkboxDel(WidgetT **widget) { +static void checkboxDel(WidgetT **widget) { CheckboxT *c = (CheckboxT *)*widget; if (c->title) free(c->title); diff --git a/client/src/gui/checkbox.h b/client/src/gui/checkbox.h index c03267d..22e7948 100644 --- a/client/src/gui/checkbox.h +++ b/client/src/gui/checkbox.h @@ -33,7 +33,6 @@ typedef struct CheckboxS { } CheckboxT; -void checkboxDel(WidgetT **widget); uint8_t checkboxGetValue(CheckboxT *checkbox); WidgetT *checkboxInit(WidgetT *widget, char *title); CheckboxT *checkboxNew(uint16_t x, uint16_t y, char *title); diff --git a/client/src/gui/desktop.c b/client/src/gui/desktop.c index 0d303e6..b86a21f 100644 --- a/client/src/gui/desktop.c +++ b/client/src/gui/desktop.c @@ -22,10 +22,11 @@ #include "window.h" +static void desktopDel(WidgetT **widget); static void desktopPaint(WidgetT *desktop, RectT pos); -void desktopDel(WidgetT **widget) { +static void desktopDel(WidgetT **widget) { DesktopT *d = (DesktopT *)*widget; vbeSurfaceDestroy(&d->base.surface); diff --git a/client/src/gui/desktop.h b/client/src/gui/desktop.h index 75b2f55..a99a17a 100644 --- a/client/src/gui/desktop.h +++ b/client/src/gui/desktop.h @@ -31,7 +31,6 @@ typedef struct DesktopS { } DesktopT; -void desktopDel(WidgetT **widget); WidgetT *desktopInit(WidgetT *desktop); DesktopT *desktopNew(void); diff --git a/client/src/gui/frame.c b/client/src/gui/frame.c index 4257f6f..933c8cd 100644 --- a/client/src/gui/frame.c +++ b/client/src/gui/frame.c @@ -21,10 +21,11 @@ #include "frame.h" +static void frameDel(WidgetT **widget); static void framePaint(WidgetT *widget, RectT pos); -void frameDel(WidgetT **widget) { +static void frameDel(WidgetT **widget) { FrameT *f = (FrameT *)*widget; if (f->title) free(f->title); diff --git a/client/src/gui/frame.h b/client/src/gui/frame.h index 794214a..51c3eb9 100644 --- a/client/src/gui/frame.h +++ b/client/src/gui/frame.h @@ -32,7 +32,6 @@ typedef struct FrameS { } FrameT; -void frameDel(WidgetT **widget); WidgetT *frameInit(WidgetT *widget, char *title); FrameT *frameNew(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title); void frameSetTitle(FrameT *frame, char *title); diff --git a/client/src/gui/gui.c b/client/src/gui/gui.c index 3aacf2a..c65e3da 100644 --- a/client/src/gui/gui.c +++ b/client/src/gui/gui.c @@ -142,8 +142,8 @@ void guiComposite() { void guiDelete(WidgetT **widget) { WidgetT *w = *widget; - size_t len = arrlenu(w->children); - size_t x = 0; + size_t len = arrlenu(w->children); + size_t x = 0; // Delete children. if (len > 0) { @@ -151,7 +151,6 @@ void guiDelete(WidgetT **widget) { guiDelete(&w->children[x]); } } - arrfree(w->children); // Delete us. w->delMethod(&w); diff --git a/client/src/gui/label.c b/client/src/gui/label.c index deae15d..8ab2f15 100644 --- a/client/src/gui/label.c +++ b/client/src/gui/label.c @@ -21,11 +21,12 @@ #include "label.h" +static void labelDel(WidgetT **widget); static void labelMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event); static void labelPaint(WidgetT *widget, RectT pos); -void labelDel(WidgetT **widget) { +static void labelDel(WidgetT **widget) { LabelT *l = (LabelT *)*widget; if (l->title) free(l->title); diff --git a/client/src/gui/label.h b/client/src/gui/label.h index a2a81c5..5a82d82 100644 --- a/client/src/gui/label.h +++ b/client/src/gui/label.h @@ -36,7 +36,6 @@ typedef struct LabelS { } LabelT; -void labelDel(WidgetT **widget); WidgetT *labelInit(WidgetT *widget, char *title); LabelT *labelNew(uint16_t x, uint16_t y, char *title); void labelSetActiveColor(LabelT *label, ColorT color); diff --git a/client/src/gui/memory.c b/client/src/gui/memory.c index 38dd6b5..37f6302 100644 --- a/client/src/gui/memory.c +++ b/client/src/gui/memory.c @@ -18,5 +18,23 @@ */ -#define STB_LEAKCHECK_IMPLEMENTATION -#include "stb_leakcheck.h" +#include "memory.h" + + +#ifdef MEMWATCH_STDIO +void memoryOutput(int c) { + putchar(c); +} +#endif + + +void memoryShutdown(void) { + // Nada. +} + + +void memoryStartup(void) { +#ifdef MEMWATCH_STDIO + mwSetOutFunc(memoryOutput); +#endif +} diff --git a/client/src/gui/memory.h b/client/src/gui/memory.h index 087baa9..8b938ea 100644 --- a/client/src/gui/memory.h +++ b/client/src/gui/memory.h @@ -22,10 +22,15 @@ #define MEMORY_H -#include "stb_leakcheck.h" +#include "os.h" + +#define MEMWATCH +#define MEMWATCH_STDIO +#include "memwatch/memwatch.h" -#define memoryLeaksShow stb_leakcheck_dumpmem +void memoryShutdown(void); +void memoryStartup(void); #endif // MEMORY_H diff --git a/client/src/gui/os.h b/client/src/gui/os.h index c62e262..ddd321b 100644 --- a/client/src/gui/os.h +++ b/client/src/gui/os.h @@ -22,6 +22,7 @@ #define OS_H +// Common platform includes. #include #include #include @@ -33,10 +34,12 @@ #ifdef __linux__ +// Linux DOS replacements. long biostime(int cmd, long newtime); #else +// DOS includes. #include #include #include @@ -47,13 +50,14 @@ long biostime(int cmd, long newtime); #endif +// Has to be after system headers in this file. +#include "memory.h" + +// Now our headers. #include "log.h" -// Has to be after system headers in this file. -//#include "memory.h" - - +// Some helper defines. #define DIVISIBLE_BY_EIGHT(x) ((((x) >> 3) << 3) == (x)) #define HIGH_BYTE(b) ((uint8_t)(((b) & 0xFF00) >> 8)) #define LOW_BYTE(b) ((uint8_t)((b) & 0x00FF)) diff --git a/client/src/gui/picture.c b/client/src/gui/picture.c index 11cdc50..d2a0a07 100644 --- a/client/src/gui/picture.c +++ b/client/src/gui/picture.c @@ -21,11 +21,12 @@ #include "picture.h" +static void pictureDel(WidgetT **widget); static void pictureMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event); static void picturePaint(WidgetT *widget, RectT pos); -void pictureDel(WidgetT **widget) { +static void pictureDel(WidgetT **widget) { PictureT *p = (PictureT *)*widget; if (p->image) imageUnload(&p->image); diff --git a/client/src/gui/picture.h b/client/src/gui/picture.h index 941a2f2..dfcb2d6 100644 --- a/client/src/gui/picture.h +++ b/client/src/gui/picture.h @@ -35,7 +35,6 @@ typedef struct PictureS { } PictureT; -void pictureDel(WidgetT **widget); WidgetT *pictureInit(WidgetT *widget, char *filename); PictureT *pictureNew(uint16_t x, uint16_t y, char *filename); void pictureSetClickHandler(PictureT *picture, widgetCallback callback); diff --git a/client/src/gui/radio.c b/client/src/gui/radio.c index 4f056f8..4680535 100644 --- a/client/src/gui/radio.c +++ b/client/src/gui/radio.c @@ -22,6 +22,7 @@ static void radioClearSelectedInGroup(WidgetT *widget, uint32_t group); +static void radioDel(WidgetT **widget); static RadioT *radioFindSelectedInGroup(WidgetT *widget, uint32_t group); static void radioMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event); static void radioPaint(WidgetT *widget, RectT pos); @@ -51,7 +52,7 @@ static void radioClearSelectedInGroup(WidgetT *widget, uint32_t group) { } -void radioDel(WidgetT **widget) { +static void radioDel(WidgetT **widget) { RadioT *r = (RadioT *)*widget; if (r->title) free(r->title); diff --git a/client/src/gui/radio.h b/client/src/gui/radio.h index 001cfc1..a423d98 100644 --- a/client/src/gui/radio.h +++ b/client/src/gui/radio.h @@ -34,7 +34,6 @@ typedef struct RadioS { } RadioT; -void radioDel(WidgetT **widget); RadioT *radioGetSelected(RadioT *radio); WidgetT *radioInit(WidgetT *widget, char *title, uint16_t group); RadioT *radioNew(uint16_t x, uint16_t y, char *title, uint16_t group); diff --git a/client/src/gui/textbox.c b/client/src/gui/textbox.c index 46606f5..38a6020 100644 --- a/client/src/gui/textbox.c +++ b/client/src/gui/textbox.c @@ -22,13 +22,14 @@ #include "timer.h" +static void textboxDel(WidgetT **widget); static void textboxFocusEvent(WidgetT *widget, uint8_t focused); static void textboxMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event); static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt); static void textboxPaint(WidgetT *widget, RectT pos); -void textboxDel(WidgetT **widget) { +static void textboxDel(WidgetT **widget) { TextboxT *t = (TextboxT *)*widget; if (t->title) free(t->title); @@ -158,7 +159,7 @@ static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extende default: // Other keys if (ascii >= 32 && ascii <= 126) { // Insert character, if room. - if (strlen(t->value) < t->maxLength - 1) { + if (strlen(t->value) < (size_t)t->maxLength - 1) { // Move existing characters over, if needed. if (t->caret + t->offset < strlen(t->value)) { for (x=strlen(t->value) + 1; x>t->caret + t->offset; x--) { @@ -189,14 +190,30 @@ static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extende static void textboxMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event) { TextboxT *t = (TextboxT *)widget; + RectT textArea; (void)x; (void)y; (void)mouse; - //***TODO*** Allow dragging/positioning text cursor with mouse. + // Allow dragging/positioning text cursor with mouse. if (event == MOUSE_EVENT_LEFT_HOLD) { - + // Where's the text display? + textArea.x = (strlen(t->title) * fontWidthGet(_guiFont)) + _guiMetric[METRIC_TEXTBOX_PADDING] + 2 + _guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING]; + textArea.y = 2 + _guiMetric[METRIC_TEXTBOX_VERTICAL_PADDING]; + textArea.w = textArea.x + t->visible * fontWidthGet(_guiFont); + textArea.h = textArea.y + fontHeightGet(_guiFont); + // Is the pointer over it? + if (x >= textArea.x && x < textArea.w && y >= textArea.y && y < textArea.h) { + // Move caret. + t->caret = (x - textArea.x) / fontWidthGet(_guiFont); + // Did we go too far? + if (t->caret + t->offset > strlen(t->value)) { + t->caret = strlen(t->value) - t->offset; + } + // Ensure we redraw. + GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY); + } } } @@ -250,14 +267,13 @@ static void textboxPaint(WidgetT *widget, RectT pos) { // Draw value. ***TODO*** This needs much more! Do it without strdup? draw = strdup(&t->value[t->offset]); - if (strlen(t->value) > t->visible) draw[t->visible] = 0; + if (strlen(draw) > t->visible) draw[t->visible] = 0; fontRender(_guiFont, draw, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], textX, textY); free(draw); // Draw cursor. if (guiFocusGet() == widget && timerQuarterSecondOn) { caretPos = textX + fontWidthGet(_guiFont) * t->caret; - //logWrite("textX %d textY %d caretPos %d\n", textX, textY, caretPos); fontRender(_guiFont, cursor, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], caretPos, textY); } @@ -271,7 +287,7 @@ void textboxSetValue(TextboxT *textbox, char *value) { strncpy(textbox->value, value, textbox->maxLength - 1); // Is this longer than the area we have to display it in? - if (strlen(textbox->value) > textbox->visible - 1) { + if (strlen(textbox->value) > (size_t)(textbox->visible - 1)) { textbox->offset = strlen(textbox->value) - (textbox->visible - 1); } diff --git a/client/src/gui/textbox.h b/client/src/gui/textbox.h index 4b9e660..7f72d0d 100644 --- a/client/src/gui/textbox.h +++ b/client/src/gui/textbox.h @@ -37,7 +37,6 @@ typedef struct TextboxS { } TextboxT; -void textboxDel(WidgetT **widget); char *textboxGetValue(TextboxT *textbox); WidgetT *textboxInit(WidgetT *widget, char *title); TextboxT *textboxNew(uint16_t x, uint16_t y, uint16_t w, char *title); diff --git a/client/src/gui/widget.c b/client/src/gui/widget.c index b403321..51d6760 100644 --- a/client/src/gui/widget.c +++ b/client/src/gui/widget.c @@ -22,6 +22,17 @@ #include "window.h" +static void widgetDel(WidgetT **widget); + + +static void widgetDel(WidgetT **widget) { + WidgetT *w = (WidgetT *)*widget; + + free(w); + w = NULL; +} + + uint16_t widgetHeightGet(WidgetT *widget) { return widget->pos.h; } @@ -46,7 +57,7 @@ WidgetT *widgetInit(WidgetT *widget, uint8_t magic, uint16_t x, uint16_t y, uint widget->children = NULL; widget->parent = NULL; widget->window = NULL; - widget->delMethod = NULL; + widget->delMethod = widgetDel; widget->focusMethod = NULL; widget->keyboardEventMethod = NULL; widget->paintMethod = NULL; diff --git a/client/src/gui/window.c b/client/src/gui/window.c index 39041bf..92c5c72 100644 --- a/client/src/gui/window.c +++ b/client/src/gui/window.c @@ -22,6 +22,7 @@ static void windowDeactivateAll(WidgetT *widget); +static void windowDel(WidgetT **widget); static void windowMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event); static void windowPaint(WidgetT *widget, RectT pos); @@ -46,7 +47,7 @@ static void windowDeactivateAll(WidgetT *widget) { } -void windowDel(WidgetT **widget) { +static void windowDel(WidgetT **widget) { WindowT *w = (WindowT *)*widget; vbeSurfaceDestroy(&w->base.surface); diff --git a/client/src/gui/window.h b/client/src/gui/window.h index 2f1065d..8d7658a 100644 --- a/client/src/gui/window.h +++ b/client/src/gui/window.h @@ -39,7 +39,6 @@ typedef struct WindowS { } WindowT; -void windowDel(WidgetT **widget); WidgetT *windowInit(WidgetT *window, char *title); WindowT *windowNew(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title); void windowSetActive(WindowT *window); diff --git a/client/src/main.c b/client/src/main.c index 084a974..4dd50a1 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -199,6 +199,8 @@ int main(int argc, char *argv[]) { char *c = NULL; int16_t x = strlen(argv[0]); + memoryStartup(); + // 0 1 2 3 4 5 6 7 8 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 printf("Kangaroo Punch MultiPlayer DOS Game Client Mark II\n"); @@ -222,6 +224,7 @@ int main(int argc, char *argv[]) { // Command line needs to have the desired resolution and color depth on it. if (argc != 4) { vbeShowInfo(); + memoryShutdown(); return 0; } xResolution = atoi(argv[1]); @@ -230,6 +233,7 @@ int main(int argc, char *argv[]) { // Do we have the video mode they asked for? if (vbeStartup(xResolution, yResolution, colorDepth)) { + memoryShutdown(); return 1; } @@ -248,11 +252,9 @@ int main(int argc, char *argv[]) { mouseShutdown(); vbeShutdown(); -#ifdef memoryLeakShow - memoryLeaksShow(); -#endif - logClose(); + memoryShutdown(); + return 0; } diff --git a/client/src/thirdparty/memwatch/FAQ b/client/src/thirdparty/memwatch/FAQ new file mode 100644 index 0000000..efcec58 --- /dev/null +++ b/client/src/thirdparty/memwatch/FAQ @@ -0,0 +1,133 @@ +Frequently Asked Questions for memwatch + +Q. I'm not getting any log file! What's wrong?? + +A. Did you define MEMWATCH when compiling all files? + Did you include memwatch.h in all the files? + If you did, then...: + + Memwatch creates the file when it initializes. If you're not + getting the log file, it's because a) memwatch is not + initializing or b) it's initializing, but can't create the + file. + + Memwatch has two functions, mwInit() and mwTerm(), that + initialize and terminate memwatch, respectively. They are + nestable. You USUALLY don't need to call mwInit() and + mwTerm(), since memwatch will auto-initialize on the first + call to a memory function, and then add mwTerm() to the + atexit() list. + + You can call mwInit() and mwTerm() manually, if it's not + initializing properly or if your system doesn't support + atexit(). Call mwInit() as soon as you can, and mwTerm() at + the logical no-error ending of your program. Call mwAbort() + if the program is stopping due to an error; this will + terminate memwatch even if more than one call to mwTerm() is + outstanding. + + If you are using C++, remember that global and static C++ + objects constructors execute before main() when considering + where to put mwInit(). Also, their destructors execute after + main(). You may want to create a global object very early + with mwInit() in the constructor and mwTerm() in the + destructor. Too bad C++ does not guarantee initialization + order for global objects. + + If this didn't help, try adding a call to mwDoFlush(1) after + mwInit(). If THAT didn't help, then memwatch is unable to + create the log file. Check write permissions. + + If you can't use a log file, you can still use memwatch by + redirecting the output to a function of your choice. See the + next question. + +Q. I'd like memwatch's output to pipe to my fave debugger! How? + +A. Call mwSetOutFunc() with the address of a "void func(int c)" + function. You should also consider doing something about + the ARI handler, see memwatch.h for more details about that. + +Q. Why isn't there any C++ support? + +A. Because C++ is for sissies! =) Just kidding. + C++ comes with overridable allocation/deallocation + built-in. You can define your own new/delete operators + for any class, and thus circumvent memwatch, or confuse + it to no end. Also, the keywords "new" and "delete" may + appear in declarations in C++, making the preprocessor + replacement approach shaky. You can do it, but it's not + very stable. + + If someone were to write a rock solid new/delete checker + for C++, there is no conflict with memwatch; use them both. + +Q. I'm getting "WILD free" errors, but the code is bug-free! + +A. If memwatch's free() recieves a pointer that wasn't allocated + by memwatch, a "WILD free" message appears. If the source of + the memory buffer is outside of memwatch (a non-standard + library function, for instance), you can use mwFree_() to + release it. mwFree_() calls free() on the pointer given if + memwatch can't recognize it, instead of blocking it. + + Another source of "WILD free" messages is that if memwatch + is terminated before all memory allocated is freed, memwatch + will have forgotten about it, and thus generate the errors. + This is commonly caused by having memwatch auto-initialize, + and then using atexit() to clean up. When auto-initializing, + memwatch registers mwTerm() with atexit(), but if mwTerm() + runs before all memory is freed, then you will get "unfreed" + and "WILD free" messages when your own atexit()-registered + cleanup code runs, and frees the memory. + +Q. I'm getting "unfreed" errors, but the code is bug-free! + +A. You can get erroneous "unfreed" messages if memwatch + terminates before all memory has been freed. Try using + mwInit() and mwTerm() instead of auto-initialization. + + If you _are_ using mwInit() and mwTerm(), it may be that + some code in your program executes before mwInit() or + after mwTerm(). Make sure that mwInit() is the first thing + executed, and mwTerm() the last. + +Q. When compiling memwatch I get these 'might get clobbered' + errors, and something about a longjmp() inside memwatch. + +A. When using gcc or egcs with the optimization to inline + functions, this warning occurs. This is because gcc and + egcs inlines memwatch's functions with setjmp/longjmp, + causing the calling functions to become unstable. + + The gcc/egcs maintainers have been informed of this + problem, but until they modify the inline optimization + so that it leaves setjmp functions alone, make sure to + compile memwatch without inline function optimizations. + + gcc 2.95.2 can be patched for this, and I have been told + it will be fixed in an upcoming version. + +Q. My program crashes with SIGSEGV or alignment errors, but + only when I compile with memwatch enabled! + +A. You are using a 64-bit (or higher) platform, and memwatch + was unable to detect and adjust for this. You'll have to + either compile with a suitable define for mwROUNDALLOC, + I suggest (number of bits / 8), or define mwROUNDALLOC + directly in memwatch.c. + + Also, please check your limits.h file for the relevant + #defines, and tell me what they are. + +Q. When I include string.h after memwatch.h, I get errors + related to strdup(), what gives? + +A. Most, but probably not all, platforms are nice about + including files multiple times, so I could probably + avoid these errors by including string.h from memwatch.h. + But since I want to be on the safe side, I don't. + + To fix this, simply include string.h before memwatch.h, + or modify memwatch.h to include string.h. + diff --git a/client/src/thirdparty/memwatch/Makefile b/client/src/thirdparty/memwatch/Makefile new file mode 100644 index 0000000..bc1be19 --- /dev/null +++ b/client/src/thirdparty/memwatch/Makefile @@ -0,0 +1,2 @@ +test: + $(CC) -DMEMWATCH -DMW_STDIO test.c memwatch.c diff --git a/client/src/thirdparty/memwatch/README b/client/src/thirdparty/memwatch/README new file mode 100644 index 0000000..aeb86ce --- /dev/null +++ b/client/src/thirdparty/memwatch/README @@ -0,0 +1,99 @@ +README for MEMWATCH 2.69 + + This file should be enough to get you started, and should be + enough for small projects. For more info, see the files USING + and the FAQ. If this is not enough, see memwatch.h, which is + well documented. + + Memwatch is licensed under the GPL from version 2.69 + onwards. Please read the file gpl.txt for more details. + + If you choose to use memwatch to validate your projects, I + would like to hear about it. Please drop me a line at + johan@linkdata.se about the project itself, the hardware, + operating system, compiler and any URL(s) you feel is + appropriate. + +***** To run the test program: + + Look at the source code for test.c first. It does some really + nasty things, and I want you to be aware of that. If memwatch + can't capture SIGSEGV (General Protection Fault for Windoze), + your program will dump core (crash for Windoze). + + Once you've done that, you can build the test program. + + Linux and other *nixes with gcc: + + gcc -o test -DMEMWATCH -DMEMWATCH_STDIO test.c memwatch.c + + Windows 95, Windows NT with MS Visual C++: + + cl -DMEMWATCH -DMEMWATCH_STDIO test.c memwatch.c + + Then simply run the test program. + + ./test + + +***** Quick-start instructions: + + 1. Make sure that memwatch.h is included in all of the + source code files. If you have an include file that + all of the source code uses, you might be able to include + memwatch.h from there. + + 2. Recompile the program with MEMWATCH defined. See your + compiler's documentation if you don't know how to do this. + The usual switch looks like "-DMEMWATCH". To have MEMWATCH + use stderr for some output (like, "Abort, Retry, Ignore?"), + please also define MW_STDIO (or MEMWATCH_STDIO, same thing). + + 3. Run the program and examine the output in the + log file "memwatch.log". If you didn't get a log file, + you probably didn't do step 1 and 2 correctly, or your + program crashed before memwatch flushed the file buffer. + To have memwatch _always_ flush the buffer, add a call + to "mwDoFlush(1)" at the top of your main function. + + 4. There is no fourth step... but remember that there + are limits to what memwatch can do, and that you need + to be aware of them: + +***** Limits to memwatch: + + Memwatch cannot catch all wild pointer writes. It can catch + those it could make itself due to your program trashing + memwatch's internal data structures. It can catch, sort of, + wild writes into No Mans Land buffers (see the header file for + more info). Anything else and you're going to get core dumped, + or data corruption if you're lucky. + + There are other limits of course, but that one is the most + serious one, and the one that you're likely to be suffering + from. + +***** Can use memwatch with XXXXX? + + Probably the answer is yes. It's been tested with several + different platforms and compilers. It may not work on yours + though... but there's only one way to find out. + +***** Need more assistance? + + I don't want e-mail on "how to program in C", or "I've got a + bug, help me". I _do_ want you to send email to me if you + find a bug in memwatch, or if it won't compile cleanly on your + system (assuming it's an ANSI-C compiler of course). + + If you need help with using memwatch, read the header file. + If, after reading the header file, you still can't resolve the + problem, please mail me with the details. + + I can be reached at "johan@linkdata.se". + + The latest version of memwatch should be found at + "http://www.linkdata.se/". + + Johan Lindh + diff --git a/client/src/thirdparty/memwatch/USING b/client/src/thirdparty/memwatch/USING new file mode 100644 index 0000000..3d78e95 --- /dev/null +++ b/client/src/thirdparty/memwatch/USING @@ -0,0 +1,213 @@ +Using memwatch +============== + +What is it? + + Memwatch is primarily a memory leak detector for C. Besides + detecting leaks, it can do a bunch of other stuff, but lets + stay to the basics. If you _really_ want to know all the + gory details, you should check out the header file, + memwatch.h, and the source code. It's actually got some + comments! (Whoa, what a concept!) + +How do I get the latest version? + + http://www.linkdata.se/sourcecode.html + ftp://ftp.linkdata.se/pub/memwatch/ + +How does it work? + + Using the C preprocessor, memwatch replaces all your + programs calls to ANSI C memory allocation functions with + calls to it's own functions, which keeps a record of all + allocations. + + Memwatch is very unobtrusive; unless the define MEMWATCH is + defined, memwatch removes all traces of itself from the + code (using the preprocessor). + + Memwatch normally writes it's data to the file + memwatch.log, but this can be overridden; see the section + on I/O, later. + +Can I use it for my C++ sources? + + You can, but it's not recommended. C++ allows individual + classes to have their own memory management, and the + preprocessor approach used by memwatch can cause havoc + with such class declarations if improperly used. + + If you have no such classes, or have them but still want + to test it, you can give it a try. + + First, re-enable the C++ support code in memwatch. + If you can't find it, you probably shouldn't be using + it. Then, in your source code, after including ALL + header files: + + #define new mwNew + #define delete mwDelete + + This will cause calls to new and delete in that source file + to be directed to memwatch. Also, be sure to read all the + text in memwatch.h regarding C++ support. + +Is this stuff thread-safe? + + I doubt it. As of version 2.66, there is rudimentary support + for threads, if you happen to be using Win32 or if you have + pthreads. Define WIN32 or MW_PTHREADS to signify this fact. + + This will cause a global mutex to be created, and memwatch + will lock it when accessing the global memory chain, but it's + still far from certified threadsafe. + +Initialization and cleanup + + In order to do it's work in a timely fashion, memwatch + needs to do some startup and cleanup work. mwInit() + initializes memwatch and mwTerm() terminates it. Memwatch + can auto-initialize, and will do so if you don't call + mwInit() yourself. If this is the case, memwatch will use + atexit() to register mwTerm() to the atexit-queue. + + The auto-init technique has a caveat; if you are using + atexit() yourself to do cleanup work, memwatch may + terminate before your program is done. To be on the safe + side, use mwInit() and mwTerm(). + + mwInit() and mwTerm() is nestable, so you can call mwInit() + several times, requiring mwTerm() to be called an equal + number of times to terminate memwatch. + + In case of the program aborting in a controlled way, you + may want to call mwAbort() instead of mwTerm(). mwAbort() + will terminate memwatch even if there are outstanding calls + to mwTerm(). + +I/O operations + + During normal operations, memwatch creates a file named + memwatch.log. Sometimes, memwatch.log can't be created; + then memwatch tries to create files name memwatNN.log, + where NN is between 01 and 99. If that fails, no log will + be produced. + + If you can't use a file log, or don't want to, no worry. + Just call mwSetOutFunc() with the address of a "void + func(int c)" function, and all output will be directed + there, character by character. + + Memwatch also has an Abort/Retry/Ignore handler that is + used when an ASSERT or VERIFY fails. The default handler + does no I/O, but automatically aborts the program. You can + use any handler you want; just send the address of a "int + func(const char*)" to mwSetAriFunc(). For more details on + that, see memwatch.h. + +TRACE/ASSERT/VERIFY macros + + Memwatch defines (if not already defined) the macros TRACE, + ASSERT and VERIFY. If you are already using macros with + these names, memwatch 2.61 and later will not override + them. Memwatch 2.61 and later will also always define the + macros mwTRACE, mwASSERT and mwVERIFY, so you can use these + to make sure you're talking to memwatch. Versions prior + to 2.61 will *OVERRIDE* existing TRACE, ASSERT and VERIFY. + + To make sure that existing TRACE, ASSERT and VERIFY macros + are preserved, you can define MW_NOTRACE, MW_NOASSERT and + MW_NOVERIFY. All versions of memwatch will abide by these. + +How slow can you go? + + Memwatch slows things down. Large allocations aren't + affected so that you can measure it, but small allocations + that would utilize a compilers small-allocator function + suddenly cannot, and so gets slowed down alot. As a worst + case, expect it to be 3-5 times slower. + + Free'ing gets hit worse, I'm afraid, as memwatch checks + a lot of stuff when freeing. Expect it to be 5-7 times + slower, no matter what the size of the allocation. + +Stress-testing the application + + You can simulate low-memory conditions using mwLimit(). + mwLimit() takes the maximum number of bytes to be + allocated; when the limit is hit, allocation requests will + fail, and a "limit" message will be logged. + + If you hit a real low-memory situation, memwatch logs that + too. Memwatch itself has some reserve memory tucked away so + it should continue running even in the worst conditions. + +Hunting down wild writes and other Nasty Things + + Wild writes are usually caused by using pointers that arent + initialized, or that were initialized, but then the memory + they points to is moved or freed. The best way to avoid + these kind of problems is to ALWAYS initialize pointers to + NULL, and after freeing a memory buffer, setting all + pointers that pointed to it to NULL. + + To aid in tracking down uninitialized pointers memwatch + zaps all memory with certain values. Recently allocated + memory (unless calloc'd, of course), contains 0xFE. + Recently freed memory contains 0xFD. So if your program + crashes when using memwatch and not without memwatch, it's + most likely because you are not initializing your allocated + buffers, or using the buffers after they've been freed. + + In the event that a wild pointer should damage memwatch's + internal data structures, memwatch employs checksums, + multiple copies of some values, and can also repair it's + own data structures. + + If you are a paranoid person, and as programmer you should + be, you can use memwatch's mwIsReadAddr() and + mwIsSafeAddr() functions to check the accessibility of + memory. These are implemented for both ANSI C systems and + Win32 systems. Just put an mwASSERT() around the check and + forget about it. + +Can I help? + + Well, sure. For instance, I like memwatch to compile + without any warnings or errors. If you are using an ANSI C + compliant compiler, and are getting warnings or errors, + please mail me the details and instructions on how to fix + them, if you can. + + Another thing you can do if you decide to use memwatch is + to mail me the name of the project(s) (and URL, if any), + hardware and operating system, compiler and what user + (organization). I will then post this info on the list of + memwatch users. + (http://www.linkdata.se/memwatchusers.html) + +Top five problems using memwatch + + 5. Passed a non-memwatch allocated pointer to memwatch's + free(). Symtom: Causes an erroneous "WILD free" log + entry to appear. Cure: Either include memwatch.h for + the file that allocates, or use mwFree_() to free it. + + 4. Relied on auto-initialization when using atexit(). + Symptom: Causes incorrect "unfreed" and "WILD free" + messages. Cure: Use mwInit() and mwTerm(). + + 3. Forgot to include memwatch.h in all files. Symptom: + Tends to generate "WILD free" and "unfreed" messages. + Cure: Make sure to include memwatch.h! + + 2. No write permissions in currect directory. Symptom: + Seems like memwatch 'just aint working'. Cure: Use + mwSetOutFunc() to redirect output. + + ...and the number one problem is... + + 1. Didn't define MEMWATCH when compiling. Symptom: + Memwatch dutifully disables itself. Cure: Try adding + -DMEMWATCH to the command line. + diff --git a/client/src/thirdparty/memwatch/gpl.txt b/client/src/thirdparty/memwatch/gpl.txt new file mode 100644 index 0000000..5b6e7c6 --- /dev/null +++ b/client/src/thirdparty/memwatch/gpl.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/client/src/thirdparty/memwatch/memwatch.c b/client/src/thirdparty/memwatch/memwatch.c new file mode 100644 index 0000000..9d712d7 --- /dev/null +++ b/client/src/thirdparty/memwatch/memwatch.c @@ -0,0 +1,2664 @@ +/* +** MEMWATCH.C +** Nonintrusive ANSI C memory leak / overwrite detection +** Copyright (C) 1992-2003 Johan Lindh +** All rights reserved. +** Version 2.71 + + This file is part of MEMWATCH. + + MEMWATCH is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + MEMWATCH is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with MEMWATCH; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +** +** 920810 JLI [1.00] +** 920830 JLI [1.10 double-free detection] +** 920912 JLI [1.15 mwPuts, mwGrab/Drop, mwLimit] +** 921022 JLI [1.20 ASSERT and VERIFY] +** 921105 JLI [1.30 C++ support and TRACE] +** 921116 JLI [1.40 mwSetOutFunc] +** 930215 JLI [1.50 modified ASSERT/VERIFY] +** 930327 JLI [1.51 better auto-init & PC-lint support] +** 930506 JLI [1.55 MemWatch class, improved C++ support] +** 930507 JLI [1.60 mwTest & CHECK()] +** 930809 JLI [1.65 Abort/Retry/Ignore] +** 930820 JLI [1.70 data dump when unfreed] +** 931016 JLI [1.72 modified C++ new/delete handling] +** 931108 JLI [1.77 mwSetAssertAction() & some small changes] +** 940110 JLI [1.80 no-mans-land alloc/checking] +** 940328 JLI [2.00 version 2.0 rewrite] +** Improved NML (no-mans-land) support. +** Improved performance (especially for free()ing!). +** Support for 'read-only' buffers (checksums) +** ^^ NOTE: I never did this... maybe I should? +** FBI (free'd block info) tagged before freed blocks +** Exporting of the mwCounter variable +** mwBreakOut() localizes debugger support +** Allocation statistics (global, per-module, per-line) +** Self-repair ability with relinking +** 950913 JLI [2.10 improved garbage handling] +** 951201 JLI [2.11 improved auto-free in emergencies] +** 960125 JLI [X.01 implemented auto-checking using mwAutoCheck()] +** 960514 JLI [2.12 undefining of existing macros] +** 960515 JLI [2.13 possibility to use default new() & delete()] +** 960516 JLI [2.20 suppression of file flushing on unfreed msgs] +** 960516 JLI [2.21 better support for using MEMWATCH with DLL's] +** 960710 JLI [X.02 multiple logs and mwFlushNow()] +** 960801 JLI [2.22 merged X.01 version with current] +** 960805 JLI [2.30 mwIsXXXXAddr() to avoid unneeded GP's] +** 960805 JLI [2.31 merged X.02 version with current] +** 961002 JLI [2.32 support for realloc() + fixed STDERR bug] +** 961222 JLI [2.40 added mwMark() & mwUnmark()] +** 970101 JLI [2.41 added over/underflow checking after failed ASSERT/VERIFY] +** 970113 JLI [2.42 added support for PC-Lint 7.00g] +** 970207 JLI [2.43 added support for strdup()] +** 970209 JLI [2.44 changed default filename to lowercase] +** 970405 JLI [2.45 fixed bug related with atexit() and some C++ compilers] +** 970723 JLI [2.46 added MW_ARI_NULLREAD flag] +** 970813 JLI [2.47 stabilized marker handling] +** 980317 JLI [2.48 ripped out C++ support; wasn't working good anyway] +** 980318 JLI [2.50 improved self-repair facilities & SIGSEGV support] +** 980417 JLI [2.51 more checks for invalid addresses] +** 980512 JLI [2.52 moved MW_ARI_NULLREAD to occur before aborting] +** 990112 JLI [2.53 added check for empty heap to mwIsOwned] +** 990217 JLI [2.55 improved the emergency repairs diagnostics and NML] +** 990224 JLI [2.56 changed ordering of members in structures] +** 990303 JLI [2.57 first maybe-fixit-for-hpux test] +** 990516 JLI [2.58 added 'static' to the definition of mwAutoInit] +** 990517 JLI [2.59 fixed some high-sensitivity warnings] +** 990610 JLI [2.60 fixed some more high-sensitivity warnings] +** 990715 JLI [2.61 changed TRACE/ASSERT/VERIFY macro names] +** 991001 JLI [2.62 added CHECK_BUFFER() and mwTestBuffer()] +** 991007 JLI [2.63 first shot at a 64-bit compatible version] +** 991009 JLI [2.64 undef's strdup() if defined, mwStrdup made const] +** 000704 JLI [2.65 added some more detection for 64-bits] +** 010502 JLI [2.66 incorporated some user fixes] +** [mwRelink() could print out garbage pointer (thanks mac@phobos.ca)] +** [added array destructor for C++ (thanks rdasilva@connecttel.com)] +** [added mutex support (thanks rdasilva@connecttel.com)] +** 010531 JLI [2.67 fix: mwMutexXXX() was declared even if MW_HAVE_MUTEX was not defined] +** 010619 JLI [2.68 fix: mwRealloc() could leave the mutex locked] +** 020918 JLI [2.69 changed to GPL, added C++ array allocation by Howard Cohen] +** 030212 JLI [2.70 mwMalloc() bug for very large allocations (4GB on 32bits)] +** 030520 JLI [2.71 added ULONG_LONG_MAX as a 64-bit detector (thanks Sami Salonen)] +*/ + +#define __MEMWATCH_C 1 + +#ifdef MW_NOCPP +#define MEMWATCH_NOCPP +#endif +#ifdef MW_STDIO +#define MEMWATCH_STDIO +#endif + +/*********************************************************************** +** Include files +***********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "memwatch.h" + +#ifndef toupper +#include +#endif + +#if defined(WIN32) || defined(__WIN32__) +#define MW_HAVE_MUTEX 1 +#include +#endif + +#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H) +#define MW_HAVE_MUTEX 1 +#include +#endif + +/*********************************************************************** +** Defines & other weird stuff +***********************************************************************/ + +/*lint -save -e767 */ +#define VERSION "2.71" /* the current version number */ +#define CHKVAL(mw) (0xFE0180L^(long)mw->count^(long)mw->size^(long)mw->line) +#define FLUSH() mwFlush() +#define TESTS(f,l) if(mwTestAlways) (void)mwTestNow(f,l,1) +#define PRECHK 0x01234567L +#define POSTCHK 0x76543210L +#define mwBUFFER_TO_MW(p) ( (mwData*) (void*) ( ((char*)p)-mwDataSize-mwOverflowZoneSize ) ) +/*lint -restore */ + +#define MW_NML 0x0001 + +#ifdef _MSC_VER +#define COMMIT "c" /* Microsoft C requires the 'c' to perform as desired */ +#else +#define COMMIT "" /* Normal ANSI */ +#endif /* _MSC_VER */ + +#ifdef __cplusplus +#define CPPTEXT "++" +#else +#define CPPTEXT "" +#endif /* __cplusplus */ + +#ifdef MEMWATCH_STDIO +#define mwSTDERR stderr +#else +#define mwSTDERR mwLog +#endif + +#ifdef MW_HAVE_MUTEX +#define MW_MUTEX_INIT() mwMutexInit() +#define MW_MUTEX_TERM() mwMutexTerm() +#define MW_MUTEX_LOCK() mwMutexLock() +#define MW_MUTEX_UNLOCK() mwMutexUnlock() +#else +#define MW_MUTEX_INIT() +#define MW_MUTEX_TERM() +#define MW_MUTEX_LOCK() +#define MW_MUTEX_UNLOCK() +#endif + +/*********************************************************************** +** If you really, really know what you're doing, +** you can predefine these things yourself. +***********************************************************************/ + +#ifndef mwBYTE_DEFINED +# if CHAR_BIT != 8 +# error need CHAR_BIT to be 8! +# else +typedef unsigned char mwBYTE; +# define mwBYTE_DEFINED 1 +# endif +#endif + +#if defined(ULONGLONG_MAX) || defined(ULLONG_MAX) || defined(_UI64_MAX) || defined(ULONG_LONG_MAX) +# define mw64BIT 1 +# define mwROUNDALLOC_DEFAULT 8 +#else +# if UINT_MAX <= 0xFFFFUL +# define mw16BIT 1 +# define mwROUNDALLOC_DEFAULT 2 +# else +# if ULONG_MAX > 0xFFFFFFFFUL +# define mw64BIT 1 +# define mwROUNDALLOC_DEFAULT 8 +# else +# define mw32BIT 1 +# define mwROUNDALLOC_DEFAULT 4 +# endif +# endif +#endif + +/* mwROUNDALLOC is the number of bytes to */ +/* round up to, to ensure that the end of */ +/* the buffer is suitable for storage of */ +/* any kind of object */ +#ifndef mwROUNDALLOC +# define mwROUNDALLOC mwROUNDALLOC_DEFAULT +#endif + +#ifndef mwDWORD_DEFINED +#if ULONG_MAX == 0xFFFFFFFFUL +typedef unsigned long mwDWORD; +#define mwDWORD_DEFINED "unsigned long" +#endif +#endif + +#ifndef mwDWORD_DEFINED +#if UINT_MAX == 0xFFFFFFFFUL +typedef unsigned int mwDWORD; +#define mwDWORD_DEFINED "unsigned int" +#endif +#endif + +#ifndef mwDWORD_DEFINED +#if USHRT_MAX == 0xFFFFFFFFUL +typedef unsigned short mwDWORD; +#define mwDWORD_DEFINED "unsigned short" +#endif +#endif + +#ifndef mwBYTE_DEFINED +#error "can't find out the correct type for a 8 bit scalar" +#endif + +#ifndef mwDWORD_DEFINED +#error "can't find out the correct type for a 32 bit scalar" +#endif + +/*********************************************************************** +** Typedefs & structures +***********************************************************************/ + +/* main data holding area, precedes actual allocation */ +typedef struct mwData_ mwData; +struct mwData_ { + mwData* prev; /* previous allocation in chain */ + mwData* next; /* next allocation in chain */ + const char* file; /* file name where allocated */ + long count; /* action count */ + long check; /* integrity check value */ +#if 0 + long crc; /* data crc value */ +#endif + size_t size; /* size of allocation */ + int line; /* line number where allocated */ + unsigned flag; /* flag word */ + }; + +/* statistics structure */ +typedef struct mwStat_ mwStat; +struct mwStat_ { + mwStat* next; /* next statistic buffer */ + const char* file; + long total; /* total bytes allocated */ + long num; /* total number of allocations */ + long max; /* max allocated at one time */ + long curr; /* current allocations */ + int line; + }; + +/* grabbing structure, 1K in size */ +typedef struct mwGrabData_ mwGrabData; +struct mwGrabData_ { + mwGrabData* next; + int type; + char blob[ 1024 - sizeof(mwGrabData*) - sizeof(int) ]; + }; + +typedef struct mwMarker_ mwMarker; +struct mwMarker_ { + void *host; + char *text; + mwMarker *next; + int level; + }; + +#if defined(WIN32) || defined(__WIN32__) +typedef HANDLE mwMutex; +#endif + +#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H) +typedef pthread_mutex_t mwMutex; +#endif + +/*********************************************************************** +** Static variables +***********************************************************************/ + +static int mwInited = 0; +static int mwInfoWritten = 0; +static int mwUseAtexit = 0; +static FILE* mwLog = NULL; +static int mwFlushing = 0; +static int mwStatLevel = MW_STAT_DEFAULT; +static int mwNML = MW_NML_DEFAULT; +static int mwFBI = 0; +static long mwAllocLimit = 0L; +static int mwUseLimit = 0; + +static long mwNumCurAlloc = 0L; +static mwData* mwHead = NULL; +static mwData* mwTail = NULL; +static int mwDataSize = 0; +static unsigned char mwOverflowZoneTemplate[] = "mEmwAtch"; +static int mwOverflowZoneSize = mwROUNDALLOC; + +static void (*mwOutFunction)(int) = NULL; +static int (*mwAriFunction)(const char*) = NULL; +static int mwAriAction = MW_ARI_ABORT; + +static char mwPrintBuf[MW_TRACE_BUFFER+8]; + +static unsigned long mwCounter = 0L; +static long mwErrors = 0L; + +static int mwTestFlags = 0; +static int mwTestAlways = 0; + +static FILE* mwLogB1 = NULL; +static int mwFlushingB1 = 0; + +static mwStat* mwStatList = NULL; +static long mwStatTotAlloc = 0L; +static long mwStatMaxAlloc = 0L; +static long mwStatNumAlloc = 0L; +static long mwStatCurAlloc = 0L; +static long mwNmlNumAlloc = 0L; +static long mwNmlCurAlloc = 0L; + +static mwGrabData* mwGrabList = NULL; +static long mwGrabSize = 0L; + +static void * mwLastFree[MW_FREE_LIST]; +static const char *mwLFfile[MW_FREE_LIST]; +static int mwLFline[MW_FREE_LIST]; +static int mwLFcur = 0; + +static mwMarker* mwFirstMark = NULL; + +static FILE* mwLogB2 = NULL; +static int mwFlushingB2 = 0; + +#ifdef MW_HAVE_MUTEX +static mwMutex mwGlobalMutex; +#endif + +/*********************************************************************** +** Static function declarations +***********************************************************************/ + +static void mwAutoInit( void ); +static FILE* mwLogR( void ); +static void mwLogW( FILE* ); +static int mwFlushR( void ); +static void mwFlushW( int ); +static void mwFlush( void ); +static void mwIncErr( void ); +static void mwUnlink( mwData*, const char* file, int line ); +static int mwRelink( mwData*, const char* file, int line ); +static int mwIsHeapOK( mwData *mw ); +static int mwIsOwned( mwData* mw, const char* file, int line ); +static int mwTestBuf( mwData* mw, const char* file, int line ); +static void mwDefaultOutFunc( int ); +static void mwWrite( const char* format, ... ); +static void mwLogFile( const char* name ); +static size_t mwFreeUp( size_t, int ); +static const void *mwTestMem( const void *, unsigned, int ); +static int mwStrCmpI( const char *s1, const char *s2 ); +static int mwTestNow( const char *file, int line, int always_invoked ); +static void mwDropAll( void ); +static const char *mwGrabType( int type ); +static unsigned mwGrab_( unsigned kb, int type, int silent ); +static unsigned mwDrop_( unsigned kb, int type, int silent ); +static int mwARI( const char* text ); +static void mwStatReport( void ); +static mwStat* mwStatGet( const char*, int, int ); +static void mwStatAlloc( size_t, const char*, int ); +static void mwStatFree( size_t, const char*, int ); +static int mwCheckOF( const void * p ); +static void mwWriteOF( void * p ); +static char mwDummy( char c ); +#ifdef MW_HAVE_MUTEX +static void mwMutexInit( void ); +static void mwMutexTerm( void ); +static void mwMutexLock( void ); +static void mwMutexUnlock( void ); +#endif + +/*********************************************************************** +** System functions +***********************************************************************/ + +void mwInit( void ) { + time_t tid; + + if( mwInited++ > 0 ) return; + + MW_MUTEX_INIT(); + + /* start a log if none is running */ + if( mwLogR() == NULL ) mwLogFile( "memwatch.log" ); + if( mwLogR() == NULL ) { + int i; + char buf[32]; + /* oops, could not open it! */ + /* probably because it's already open */ + /* so we try some other names */ + for( i=1; i<100; i++ ) { + sprintf( buf, "memwat%02d.log", i ); + mwLogFile( buf ); + if( mwLogR() != NULL ) break; + } + } + + /* initialize the statistics */ + mwStatList = NULL; + mwStatTotAlloc = 0L; + mwStatCurAlloc = 0L; + mwStatMaxAlloc = 0L; + mwStatNumAlloc = 0L; + mwNmlCurAlloc = 0L; + mwNmlNumAlloc = 0L; + + /* calculate the buffer size to use for a mwData */ + mwDataSize = sizeof(mwData); + while( mwDataSize % mwROUNDALLOC ) mwDataSize ++; + + /* write informational header if needed */ + if( !mwInfoWritten ) { + mwInfoWritten = 1; + (void) time( &tid ); + mwWrite( + "\n=============" + " MEMWATCH " VERSION " Copyright (C) 1992-1999 Johan Lindh " + "=============\n"); + mwWrite( "\nStarted at %s\n", ctime( &tid ) ); + +/**************************************************************** Generic */ + mwWrite( "Modes: " ); +#ifdef mwNew + mwWrite( "C++ " ); +#endif /* mwNew */ +#ifdef __STDC__ + mwWrite( "__STDC__ " ); +#endif /* __STDC__ */ +#ifdef mw16BIT + mwWrite( "16-bit " ); +#endif +#ifdef mw32BIT + mwWrite( "32-bit " ); +#endif +#ifdef mw64BIT + mwWrite( "64-bit " ); +#endif + mwWrite( "mwDWORD==(" mwDWORD_DEFINED ")\n" ); + mwWrite( "mwROUNDALLOC==%d sizeof(mwData)==%d mwDataSize==%d\n", + mwROUNDALLOC, sizeof(mwData), mwDataSize ); +/**************************************************************** Generic */ + +/************************************************************ Microsoft C */ +#ifdef _MSC_VER + mwWrite( "Compiled using Microsoft C" CPPTEXT + " %d.%02d\n", _MSC_VER / 100, _MSC_VER % 100 ); +#endif /* _MSC_VER */ +/************************************************************ Microsoft C */ + +/************************************************************** Borland C */ +#ifdef __BORLANDC__ + mwWrite( "Compiled using Borland C" +#ifdef __cplusplus + "++ %d.%01d\n", __BCPLUSPLUS__/0x100, (__BCPLUSPLUS__%0x100)/0x10 ); +#else + " %d.%01d\n", __BORLANDC__/0x100, (__BORLANDC__%0x100)/0x10 ); +#endif /* __cplusplus */ +#endif /* __BORLANDC__ */ +/************************************************************** Borland C */ + +/************************************************************** Watcom C */ +#ifdef __WATCOMC__ + mwWrite( "Compiled using Watcom C %d.%02d ", + __WATCOMC__/100, __WATCOMC__%100 ); +#ifdef __FLAT__ + mwWrite( "(32-bit flat model)" ); +#endif /* __FLAT__ */ + mwWrite( "\n" ); +#endif /* __WATCOMC__ */ +/************************************************************** Watcom C */ + + mwWrite( "\n" ); + FLUSH(); + } + + if( mwUseAtexit ) (void) atexit( mwAbort ); + return; + } + +void mwAbort( void ) { + mwData *mw; + mwMarker *mrk; + char *data; + time_t tid; + int c, i, j; + int errors; + + tid = time( NULL ); + mwWrite( "\nStopped at %s\n", ctime( &tid) ); + + if( !mwInited ) + mwWrite( "internal: mwAbort(): MEMWATCH not initialized!\n" ); + + /* release the grab list */ + mwDropAll(); + + /* report mwMarked items */ + while( mwFirstMark ) { + mrk = mwFirstMark->next; + mwWrite( "mark: %p: %s\n", mwFirstMark->host, mwFirstMark->text ); + free( mwFirstMark->text ); + free( mwFirstMark ); + mwFirstMark = mrk; + mwErrors ++; + } + + /* release all still allocated memory */ + errors = 0; + while( mwHead != NULL && errors < 3 ) { + if( !mwIsOwned(mwHead, __FILE__, __LINE__ ) ) { + if( errors < 3 ) + { + errors ++; + mwWrite( "internal: NML/unfreed scan restarting\n" ); + FLUSH(); + mwHead = mwHead; + continue; + } + mwWrite( "internal: NML/unfreed scan aborted, heap too damaged\n" ); + FLUSH(); + break; + } + mwFlushW(0); + if( !(mwHead->flag & MW_NML) ) { + mwErrors++; + data = ((char*)mwHead)+mwDataSize; + mwWrite( "unfreed: <%ld> %s(%d), %ld bytes at %p ", + mwHead->count, mwHead->file, mwHead->line, (long)mwHead->size, data+mwOverflowZoneSize ); + if( mwCheckOF( data ) ) { + mwWrite( "[underflowed] "); + FLUSH(); + } + if( mwCheckOF( (data+mwOverflowZoneSize+mwHead->size) ) ) { + mwWrite( "[overflowed] "); + FLUSH(); + } + mwWrite( " \t{" ); + j = 16; if( mwHead->size < 16 ) j = (int) mwHead->size; + for( i=0;i<16;i++ ) { + if( i 126 ) c = '.'; + mwWrite( "%c", c ); + } + mwWrite( "}\n" ); + mw = mwHead; + mwUnlink( mw, __FILE__, __LINE__ ); + free( mw ); + } + else { + data = ((char*)mwHead) + mwDataSize + mwOverflowZoneSize; + if( mwTestMem( data, mwHead->size, MW_VAL_NML ) ) { + mwErrors++; + mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n", + mwHead->count, data + mwOverflowZoneSize, mwHead->file, mwHead->line ); + FLUSH(); + } + mwNmlNumAlloc --; + mwNmlCurAlloc -= mwHead->size; + mw = mwHead; + mwUnlink( mw, __FILE__, __LINE__ ); + free( mw ); + } + } + + if( mwNmlNumAlloc ) mwWrite("internal: NoMansLand block counter %ld, not zero\n", mwNmlNumAlloc ); + if( mwNmlCurAlloc ) mwWrite("internal: NoMansLand byte counter %ld, not zero\n", mwNmlCurAlloc ); + + /* report statistics */ + mwStatReport(); + FLUSH(); + + mwInited = 0; + mwHead = mwTail = NULL; + if( mwErrors ) + fprintf(mwSTDERR,"MEMWATCH detected %ld anomalies\n",mwErrors); + mwLogFile( NULL ); + mwErrors = 0; + + MW_MUTEX_TERM(); + + } + +void mwTerm( void ) { + if( mwInited == 1 ) + { + mwAbort(); + return; + } + if( !mwInited ) + mwWrite("internal: mwTerm(): MEMWATCH has not been started!\n"); + else + mwInited --; + } + +void mwStatistics( int level ) +{ + mwAutoInit(); + if( level<0 ) level=0; + if( mwStatLevel != level ) + { + mwWrite( "statistics: now collecting on a %s basis\n", + level<1?"global":(level<2?"module":"line") ); + mwStatLevel = level; + } +} + +void mwAutoCheck( int onoff ) { + mwAutoInit(); + mwTestAlways = onoff; + if( onoff ) mwTestFlags = MW_TEST_ALL; + } + +void mwSetOutFunc( void (*func)(int) ) { + mwAutoInit(); + mwOutFunction = func; + } + +static void mwWriteOF( void *p ) +{ + int i; + unsigned char *ptr; + ptr = (unsigned char*) p; + for( i=0; inext is not always set? +*/ +void * mwMark( void *p, const char *desc, const char *file, unsigned line ) { + mwMarker *mrk; + unsigned n, isnew; + char *buf; + int tot, oflow = 0; + char wherebuf[128]; + + mwAutoInit(); + TESTS(NULL,0); + + if( desc == NULL ) desc = "unknown"; + if( file == NULL ) file = "unknown"; + + tot = sprintf( wherebuf, "%.48s called from %s(%d)", desc, file, line ); + if( tot >= (int)sizeof(wherebuf) ) { wherebuf[sizeof(wherebuf)-1] = 0; oflow = 1; } + + if( p == NULL ) { + mwWrite("mark: %s(%d), no mark for NULL:'%s' may be set\n", file, line, desc ); + return p; + } + + if( mwFirstMark != NULL && !mwIsReadAddr( mwFirstMark, sizeof( mwMarker ) ) ) + { + mwWrite("mark: %s(%d), mwFirstMark (%p) is trashed, can't mark for %s\n", + file, line, mwFirstMark, desc ); + return p; + } + + for( mrk=mwFirstMark; mrk; mrk=mrk->next ) + { + if( mrk->next != NULL && !mwIsReadAddr( mrk->next, sizeof( mwMarker ) ) ) + { + mwWrite("mark: %s(%d), mark(%p)->next(%p) is trashed, can't mark for %s\n", + file, line, mrk, mrk->next, desc ); + return p; + } + if( mrk->host == p ) break; + } + + if( mrk == NULL ) { + isnew = 1; + mrk = (mwMarker*) malloc( sizeof( mwMarker ) ); + if( mrk == NULL ) { + mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc ); + return p; + } + mrk->next = NULL; + n = 0; + } + else { + isnew = 0; + n = strlen( mrk->text ); + } + + n += strlen( wherebuf ); + buf = (char*) malloc( n+3 ); + if( buf == NULL ) { + if( isnew ) free( mrk ); + mwWrite("mark: %s(%d), no mark for %p:'%s', out of memory\n", file, line, p, desc ); + return p; + } + + if( isnew ) { + memcpy( buf, wherebuf, n+1 ); + mrk->next = mwFirstMark; + mrk->host = p; + mrk->text = buf; + mrk->level = 1; + mwFirstMark = mrk; + } + else { + strcpy( buf, mrk->text ); + strcat( buf, ", " ); + strcat( buf, wherebuf ); + free( mrk->text ); + mrk->text = buf; + mrk->level ++; + } + + if( oflow ) { + mwIncErr(); + mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" ); + } + return p; + } + +void* mwUnmark( void *p, const char *file, unsigned line ) { + mwMarker *mrk, *prv; + mrk = mwFirstMark; + prv = NULL; + while( mrk ) { + if( mrk->host == p ) { + if( mrk->level < 2 ) { + if( prv ) prv->next = mrk->next; + else mwFirstMark = mrk->next; + free( mrk->text ); + free( mrk ); + return p; + } + mrk->level --; + return p; + } + prv = mrk; + mrk = mrk->next; + } + mwWrite("mark: %s(%d), no mark found for %p\n", file, line, p ); + return p; + } + + +/*********************************************************************** +** Abort/Retry/Ignore handlers +***********************************************************************/ + +static int mwARI( const char *estr ) { + char inbuf[81]; + int c; + fprintf(mwSTDERR, "\n%s\nMEMWATCH: Abort, Retry or Ignore? ", estr); + (void) fgets(inbuf,sizeof(inbuf),stdin); + for( c=0; inbuf[c] && inbuf[c] <= ' '; c++ ) ; + c = inbuf[c]; + if( c == 'R' || c == 'r' ) { + mwBreakOut( estr ); + return MW_ARI_RETRY; + } + if( c == 'I' || c == 'i' ) return MW_ARI_IGNORE; + return MW_ARI_ABORT; + } + +/* standard ARI handler (exported) */ +int mwAriHandler( const char *estr ) { + mwAutoInit(); + return mwARI( estr ); + } + +/* used to set the ARI function */ +void mwSetAriFunc( int (*func)(const char *) ) { + mwAutoInit(); + mwAriFunction = func; + } + +/*********************************************************************** +** Allocation handlers +***********************************************************************/ + +void* mwMalloc( size_t size, const char* file, int line) { + size_t needed; + mwData *mw; + char *ptr; + void *p; + + mwAutoInit(); + + MW_MUTEX_LOCK(); + + TESTS(file,line); + + mwCounter ++; + needed = mwDataSize + mwOverflowZoneSize*2 + size; + if( needed < size ) + { + /* theoretical case: req size + mw overhead exceeded size_t limits */ + return NULL; + } + + /* if this allocation would violate the limit, fail it */ + if( mwUseLimit && ((long)size + mwStatCurAlloc > mwAllocLimit) ) { + mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n", + mwCounter, file, line, (long)size, mwAllocLimit - mwStatCurAlloc ); + mwIncErr(); + FLUSH(); + MW_MUTEX_UNLOCK(); + return NULL; + } + + mw = (mwData*) malloc( needed ); + if( mw == NULL ) { + if( mwFreeUp(needed,0) >= needed ) { + mw = (mwData*) malloc(needed); + if( mw == NULL ) { + mwWrite( "internal: mwFreeUp(%u) reported success, but malloc() fails\n", needed ); + mwIncErr(); + FLUSH(); + } + } + if( mw == NULL ) { + mwWrite( "fail: <%ld> %s(%d), %ld wanted %ld allocated\n", + mwCounter, file, line, (long)size, mwStatCurAlloc ); + mwIncErr(); + FLUSH(); + MW_MUTEX_UNLOCK(); + return NULL; + } + } + + mw->count = mwCounter; + mw->prev = NULL; + mw->next = mwHead; + mw->file = file; + mw->size = size; + mw->line = line; + mw->flag = 0; + mw->check = CHKVAL(mw); + + if( mwHead ) mwHead->prev = mw; + mwHead = mw; + if( mwTail == NULL ) mwTail = mw; + + ptr = ((char*)mw) + mwDataSize; + mwWriteOF( ptr ); /* '*(long*)ptr = PRECHK;' */ + ptr += mwOverflowZoneSize; + p = ptr; + memset( ptr, MW_VAL_NEW, size ); + ptr += size; + mwWriteOF( ptr ); /* '*(long*)ptr = POSTCHK;' */ + + mwNumCurAlloc ++; + mwStatCurAlloc += (long) size; + mwStatTotAlloc += (long) size; + if( mwStatCurAlloc > mwStatMaxAlloc ) + mwStatMaxAlloc = mwStatCurAlloc; + mwStatNumAlloc ++; + + if( mwStatLevel ) mwStatAlloc( size, file, line ); + + MW_MUTEX_UNLOCK(); + return p; + } + +void* mwRealloc( void *p, size_t size, const char* file, int line) { + int oldUseLimit, i; + mwData *mw; + char *ptr; + + mwAutoInit(); + + if( p == NULL ) return mwMalloc( size, file, line ); + if( size == 0 ) { mwFree( p, file, line ); return NULL; } + + MW_MUTEX_LOCK(); + + /* do the quick ownership test */ + mw = (mwData*) mwBUFFER_TO_MW( p ); + if( mwIsOwned( mw, file, line ) ) { + + /* if the buffer is an NML, treat this as a double-free */ + if( mw->flag & MW_NML ) + { + mwIncErr(); + if( *((unsigned char*)(mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML ) + { + mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n", + mwCounter, file, line, mw ); + } + goto check_dbl_free; + } + + /* if this allocation would violate the limit, fail it */ + if( mwUseLimit && ((long)size + mwStatCurAlloc - (long)mw->size > mwAllocLimit) ) { + TESTS(file,line); + mwCounter ++; + mwWrite( "limit fail: <%ld> %s(%d), %ld wanted %ld available\n", + mwCounter, file, line, (unsigned long)size - mw->size, mwAllocLimit - mwStatCurAlloc ); + mwIncErr(); + FLUSH(); + MW_MUTEX_UNLOCK(); + return NULL; + } + + /* fake realloc operation */ + oldUseLimit = mwUseLimit; + mwUseLimit = 0; + ptr = (char*) mwMalloc( size, file, line ); + if( ptr != NULL ) { + if( size < mw->size ) + memcpy( ptr, p, size ); + else + memcpy( ptr, p, mw->size ); + mwFree( p, file, line ); + } + mwUseLimit = oldUseLimit; + MW_MUTEX_UNLOCK(); + return (void*) ptr; + } + + /* Unknown pointer! */ + + /* using free'd pointer? */ +check_dbl_free: + for(i=0;i %s(%d), %p was" + " freed from %s(%d)\n", + mwCounter, file, line, p, + mwLFfile[i], mwLFline[i] ); + FLUSH(); + MW_MUTEX_UNLOCK(); + return NULL; + } + } + + /* some weird pointer */ + mwIncErr(); + mwWrite( "realloc: <%ld> %s(%d), unknown pointer %p\n", + mwCounter, file, line, p ); + FLUSH(); + MW_MUTEX_UNLOCK(); + return NULL; + } + +char *mwStrdup( const char* str, const char* file, int line ) { + size_t len; + char *newstring; + + MW_MUTEX_LOCK(); + + if( str == NULL ) { + mwIncErr(); + mwWrite( "strdup: <%ld> %s(%d), strdup(NULL) called\n", + mwCounter, file, line ); + FLUSH(); + MW_MUTEX_UNLOCK(); + return NULL; + } + + len = strlen( str ) + 1; + newstring = (char*) mwMalloc( len, file, line ); + if( newstring != NULL ) memcpy( newstring, str, len ); + MW_MUTEX_UNLOCK(); + return newstring; + } + +void mwFree( void* p, const char* file, int line ) { + int i; + mwData* mw; + char buffer[ sizeof(mwData) + (mwROUNDALLOC*3) + 64 ]; + + /* this code is in support of C++ delete */ + if( file == NULL ) { + mwFree_( p ); + MW_MUTEX_UNLOCK(); + return; + } + + mwAutoInit(); + + MW_MUTEX_LOCK(); + TESTS(file,line); + mwCounter ++; + + /* on NULL free, write a warning and return */ + if( p == NULL ) { + mwWrite( "NULL free: <%ld> %s(%d), NULL pointer free'd\n", + mwCounter, file, line ); + FLUSH(); + MW_MUTEX_UNLOCK(); + return; + } + + /* do the quick ownership test */ + mw = (mwData*) mwBUFFER_TO_MW( p ); + + if( mwIsOwned( mw, file, line ) ) { + (void) mwTestBuf( mw, file, line ); + + /* if the buffer is an NML, treat this as a double-free */ + if( mw->flag & MW_NML ) + { + if( *(((unsigned char*)mw)+mwDataSize+mwOverflowZoneSize) != MW_VAL_NML ) + { + mwWrite( "internal: <%ld> %s(%d), no-mans-land MW-%p is corrupted\n", + mwCounter, file, line, mw ); + } + goto check_dbl_free; + } + + /* update the statistics */ + mwNumCurAlloc --; + mwStatCurAlloc -= (long) mw->size; + if( mwStatLevel ) mwStatFree( mw->size, mw->file, mw->line ); + + /* we should either free the allocation or keep it as NML */ + if( mwNML ) { + mw->flag |= MW_NML; + mwNmlNumAlloc ++; + mwNmlCurAlloc += (long) mw->size; + memset( ((char*)mw)+mwDataSize+mwOverflowZoneSize, MW_VAL_NML, mw->size ); + } + else { + /* unlink the allocation, and enter the post-free data */ + mwUnlink( mw, file, line ); + memset( mw, MW_VAL_DEL, + mw->size + mwDataSize+mwOverflowZoneSize+mwOverflowZoneSize ); + if( mwFBI ) { + memset( mw, '.', mwDataSize + mwOverflowZoneSize ); + sprintf( buffer, "FBI<%ld>%s(%d)", mwCounter, file, line ); + strncpy( (char*)(void*)mw, buffer, mwDataSize + mwOverflowZoneSize ); + } + free( mw ); + } + + /* add the pointer to the last-free track */ + mwLFfile[ mwLFcur ] = file; + mwLFline[ mwLFcur ] = line; + mwLastFree[ mwLFcur++ ] = p; + if( mwLFcur == MW_FREE_LIST ) mwLFcur = 0; + + MW_MUTEX_UNLOCK(); + return; + } + + /* check for double-freeing */ +check_dbl_free: + for(i=0;i %s(%d), %p was" + " freed from %s(%d)\n", + mwCounter, file, line, p, + mwLFfile[i], mwLFline[i] ); + FLUSH(); + MW_MUTEX_UNLOCK(); + return; + } + } + + /* some weird pointer... block the free */ + mwIncErr(); + mwWrite( "WILD free: <%ld> %s(%d), unknown pointer %p\n", + mwCounter, file, line, p ); + FLUSH(); + MW_MUTEX_UNLOCK(); + return; + } + +void* mwCalloc( size_t a, size_t b, const char *file, int line ) { + void *p; + size_t size = a * b; + p = mwMalloc( size, file, line ); + if( p == NULL ) return NULL; + memset( p, 0, size ); + return p; + } + +void mwFree_( void *p ) { + MW_MUTEX_LOCK(); + TESTS(NULL,0); + MW_MUTEX_UNLOCK(); + free(p); + } + +void* mwMalloc_( size_t size ) { + MW_MUTEX_LOCK(); + TESTS(NULL,0); + MW_MUTEX_UNLOCK(); + return malloc( size ); + } + +void* mwRealloc_( void *p, size_t size ) { + MW_MUTEX_LOCK(); + TESTS(NULL,0); + MW_MUTEX_UNLOCK(); + return realloc( p, size ); + } + +void* mwCalloc_( size_t a, size_t b ) { + MW_MUTEX_LOCK(); + TESTS(NULL,0); + MW_MUTEX_UNLOCK(); + return calloc( a, b ); + } + +void mwFlushNow( void ) { + if( mwLogR() ) fflush( mwLogR() ); + return; + } + +void mwDoFlush( int onoff ) { + mwFlushW( onoff<1?0:onoff ); + if( onoff ) if( mwLogR() ) fflush( mwLogR() ); + return; + } + +void mwLimit( long lim ) { + TESTS(NULL,0); + mwWrite("limit: old limit = "); + if( !mwAllocLimit ) mwWrite( "none" ); + else mwWrite( "%ld bytes", mwAllocLimit ); + mwWrite( ", new limit = "); + if( !lim ) { + mwWrite( "none\n" ); + mwUseLimit = 0; + } + else { + mwWrite( "%ld bytes\n", lim ); + mwUseLimit = 1; + } + mwAllocLimit = lim; + FLUSH(); + } + +void mwSetAriAction( int action ) { + MW_MUTEX_LOCK(); + TESTS(NULL,0); + mwAriAction = action; + MW_MUTEX_UNLOCK(); + return; + } + +int mwAssert( int exp, const char *exps, const char *fn, int ln ) { + int i; + char buffer[MW_TRACE_BUFFER+8]; + if( exp ) { + return 0; + } + mwAutoInit(); + MW_MUTEX_LOCK(); + TESTS(fn,ln); + mwIncErr(); + mwCounter++; + mwWrite( "assert trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps ); + if( mwAriFunction != NULL ) { + sprintf( buffer, "MEMWATCH: assert trap: %s(%d), %s", fn, ln, exps ); + i = (*mwAriFunction)(buffer); + switch( i ) { + case MW_ARI_IGNORE: + mwWrite( "assert trap: <%ld> IGNORED - execution continues\n", mwCounter ); + MW_MUTEX_UNLOCK(); + return 0; + case MW_ARI_RETRY: + mwWrite( "assert trap: <%ld> RETRY - executing again\n", mwCounter ); + MW_MUTEX_UNLOCK(); + return 1; + } + } + else { + if( mwAriAction & MW_ARI_IGNORE ) { + mwWrite( "assert trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter ); + MW_MUTEX_UNLOCK(); + return 0; + } + fprintf(mwSTDERR,"\nMEMWATCH: assert trap: %s(%d), %s\n", fn, ln, exps ); + } + + FLUSH(); + (void) mwTestNow( fn, ln, 1 ); + FLUSH(); + + if( mwAriAction & MW_ARI_NULLREAD ) { + /* This is made in an attempt to kick in */ + /* any debuggers or OS stack traces */ + FLUSH(); + /*lint -save -e413 */ + i = *((int*)NULL); + mwDummy( (char)i ); + /*lint -restore */ + } + + MW_MUTEX_UNLOCK(); + exit(255); + /* NOT REACHED - the return statement is in to keep */ + /* stupid compilers from squeaking about differing return modes. */ + /* Smart compilers instead say 'code unreachable...' */ + /*lint -save -e527 */ + return 0; + /*lint -restore */ + } + +int mwVerify( int exp, const char *exps, const char *fn, int ln ) { + int i; + char buffer[MW_TRACE_BUFFER+8]; + if( exp ) { + return 0; + } + mwAutoInit(); + MW_MUTEX_LOCK(); + TESTS(fn,ln); + mwIncErr(); + mwCounter++; + mwWrite( "verify trap: <%ld> %s(%d), %s\n", mwCounter, fn, ln, exps ); + if( mwAriFunction != NULL ) { + sprintf( buffer, "MEMWATCH: verify trap: %s(%d), %s", fn, ln, exps ); + i = (*mwAriFunction)(buffer); + if( i == 0 ) { + mwWrite( "verify trap: <%ld> IGNORED - execution continues\n", mwCounter ); + MW_MUTEX_UNLOCK(); + return 0; + } + if( i == 1 ) { + mwWrite( "verify trap: <%ld> RETRY - executing again\n", mwCounter ); + MW_MUTEX_UNLOCK(); + return 1; + } + } + else { + if( mwAriAction & MW_ARI_NULLREAD ) { + /* This is made in an attempt to kick in */ + /* any debuggers or OS stack traces */ + FLUSH(); + /*lint -save -e413 */ + i = *((int*)NULL); + mwDummy( (char)i ); + /*lint -restore */ + } + if( mwAriAction & MW_ARI_IGNORE ) { + mwWrite( "verify trap: <%ld> AUTO IGNORED - execution continues\n", mwCounter ); + MW_MUTEX_UNLOCK(); + return 0; + } + fprintf(mwSTDERR,"\nMEMWATCH: verify trap: %s(%d), %s\n", fn, ln, exps ); + } + FLUSH(); + (void) mwTestNow( fn, ln, 1 ); + FLUSH(); + MW_MUTEX_UNLOCK(); + exit(255); + /* NOT REACHED - the return statement is in to keep */ + /* stupid compilers from squeaking about differing return modes. */ + /* Smart compilers instead say 'code unreachable...' */ + /*lint -save -e527 */ + return 0; + /*lint -restore */ + } + +void mwTrace( const char *format, ... ) { + int tot, oflow = 0; + va_list mark; + + mwAutoInit(); + MW_MUTEX_LOCK(); + TESTS(NULL,0); + if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc; + + va_start( mark, format ); + tot = vsprintf( mwPrintBuf, format, mark ); + va_end( mark ); + if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; } + for(tot=0;mwPrintBuf[tot];tot++) + (*mwOutFunction)( mwPrintBuf[tot] ); + if( oflow ) { + mwIncErr(); + mwTrace( " [WARNING: OUTPUT BUFFER OVERFLOW - SYSTEM UNSTABLE]\n" ); + } + + FLUSH(); + MW_MUTEX_UNLOCK(); + } + + +/*********************************************************************** +** Grab & Drop +***********************************************************************/ + +unsigned mwGrab( unsigned kb ) { + TESTS(NULL,0); + return mwGrab_( kb, MW_VAL_GRB, 0 ); + } + +unsigned mwDrop( unsigned kb ) { + TESTS(NULL,0); + return mwDrop_( kb, MW_VAL_GRB, 0 ); + } + +static void mwDropAll() { + TESTS(NULL,0); + (void) mwDrop_( 0, MW_VAL_GRB, 0 ); + (void) mwDrop_( 0, MW_VAL_NML, 0 ); + if( mwGrabList != NULL ) + mwWrite( "internal: the grab list is not empty after mwDropAll()\n"); + } + +static const char *mwGrabType( int type ) { + switch( type ) { + case MW_VAL_GRB: + return "grabbed"; + case MW_VAL_NML: + return "no-mans-land"; + default: + /* do nothing */ + ; + } + return ""; + } + +static unsigned mwGrab_( unsigned kb, int type, int silent ) { + unsigned i = kb; + mwGrabData *gd; + if( !kb ) i = kb = 65000U; + + for(;kb;kb--) { + if( mwUseLimit && + (mwStatCurAlloc + mwGrabSize + (long)sizeof(mwGrabData) > mwAllocLimit) ) { + if( !silent ) { + mwWrite("grabbed: all allowed memory to %s (%u kb)\n", + mwGrabType(type), i-kb); + FLUSH(); + } + return i-kb; + } + gd = (mwGrabData*) malloc( sizeof(mwGrabData) ); + if( gd == NULL ) { + if( !silent ) { + mwWrite("grabbed: all available memory to %s (%u kb)\n", + mwGrabType(type), i-kb); + FLUSH(); + } + return i-kb; + } + mwGrabSize += (long) sizeof(mwGrabData); + gd->next = mwGrabList; + memset( gd->blob, type, sizeof(gd->blob) ); + gd->type = type; + mwGrabList = gd; + } + if( !silent ) { + mwWrite("grabbed: %u kilobytes of %s memory\n", i, mwGrabType(type) ); + FLUSH(); + } + return i; + } + +static unsigned mwDrop_( unsigned kb, int type, int silent ) { + unsigned i = kb; + mwGrabData *gd,*tmp,*pr; + const void *p; + + if( mwGrabList == NULL && kb == 0 ) return 0; + if( !kb ) i = kb = 60000U; + + pr = NULL; + gd = mwGrabList; + for(;kb;) { + if( gd == NULL ) { + if( i-kb > 0 && !silent ) { + mwWrite("dropped: all %s memory (%u kb)\n", mwGrabType(type), i-kb); + FLUSH(); + } + return i-kb; + } + if( gd->type == type ) { + if( pr ) pr->next = gd->next; + kb --; + tmp = gd; + if( mwGrabList == gd ) mwGrabList = gd->next; + gd = gd->next; + p = mwTestMem( tmp->blob, sizeof( tmp->blob ), type ); + if( p != NULL ) { + mwWrite( "wild pointer: <%ld> %s memory hit at %p\n", + mwCounter, mwGrabType(type), p ); + FLUSH(); + } + mwGrabSize -= (long) sizeof(mwGrabData); + free( tmp ); + } + else { + pr = gd; + gd = gd->next; + } + } + if( !silent ) { + mwWrite("dropped: %u kilobytes of %s memory\n", i, mwGrabType(type) ); + FLUSH(); + } + return i; + } + +/*********************************************************************** +** No-Mans-Land +***********************************************************************/ + +void mwNoMansLand( int level ) { + mwAutoInit(); + TESTS(NULL,0); + switch( level ) { + case MW_NML_NONE: + (void) mwDrop_( 0, MW_VAL_NML, 0 ); + break; + case MW_NML_FREE: + break; + case MW_NML_ALL: + (void) mwGrab_( 0, MW_VAL_NML, 0 ); + break; + default: + return; + } + mwNML = level; + } + +/*********************************************************************** +** Static functions +***********************************************************************/ + +static void mwAutoInit( void ) +{ + if( mwInited ) return; + mwUseAtexit = 1; + mwInit(); + return; +} + +static FILE *mwLogR() { + if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) return mwLog; + if( mwLog == mwLogB1 ) mwLogB2 = mwLog; + if( mwLog == mwLogB2 ) mwLogB1 = mwLog; + if( mwLogB1 == mwLogB2 ) mwLog = mwLogB1; + if( (mwLog == mwLogB1) && (mwLog == mwLogB2) ) { + mwWrite("internal: log file handle damaged and recovered\n"); + FLUSH(); + return mwLog; + } + fprintf(mwSTDERR,"\nMEMWATCH: log file handle destroyed, using mwSTDERR\n" ); + mwLog = mwLogB1 = mwLogB2 = mwSTDERR; + return mwSTDERR; + } + +static void mwLogW( FILE *p ) { + mwLog = mwLogB1 = mwLogB2 = p; + } + +static int mwFlushR() { + if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) return mwFlushing; + if( mwFlushing == mwFlushingB1 ) mwFlushingB2 = mwFlushing; + if( mwFlushing == mwFlushingB2 ) mwFlushingB1 = mwFlushing; + if( mwFlushingB1 == mwFlushingB2 ) mwFlushing = mwFlushingB1; + if( (mwFlushing == mwFlushingB1) && (mwFlushing == mwFlushingB2) ) { + mwWrite("internal: flushing flag damaged and recovered\n"); + FLUSH(); + return mwFlushing; + } + mwWrite("internal: flushing flag destroyed, so set to true\n"); + mwFlushing = mwFlushingB1 = mwFlushingB2 = 1; + return 1; + } + +static void mwFlushW( int n ) { + mwFlushing = mwFlushingB1 = mwFlushingB2 = n; + } + +static void mwIncErr() { + mwErrors++; + mwFlushW( mwFlushR()+1 ); + FLUSH(); + } + +static void mwFlush() { + if( mwLogR() == NULL ) return; +#ifdef MW_FLUSH + fflush( mwLogR() ); +#else + if( mwFlushR() ) fflush( mwLogR() ); +#endif + return; + } + +static void mwUnlink( mwData* mw, const char* file, int line ) { + if( mw->prev == NULL ) { + if( mwHead != mw ) + mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 NULL, but not head\n", + mwCounter, file, line, mw ); + mwHead = mw->next; + } + else { + if( mw->prev->next != mw ) + mwWrite( "internal: <%ld> %s(%d), MW-%p: link1 failure\n", + mwCounter, file, line, mw ); + else mw->prev->next = mw->next; + } + if( mw->next == NULL ) { + if( mwTail != mw ) + mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 NULL, but not tail\n", + mwCounter, file, line, mw ); + mwTail = mw->prev; + } + else { + if( mw->next->prev != mw ) + mwWrite( "internal: <%ld> %s(%d), MW-%p: link2 failure\n", + mwCounter, file, line, mw ); + else mw->next->prev = mw->prev; + } + } + +/* +** Relinking tries to repair a damaged mw block. +** Returns nonzero if it thinks it successfully +** repaired the heap chain. +*/ +static int mwRelink( mwData* mw, const char* file, int line ) { + int fails; + mwData *mw1, *mw2; + long count, size; + mwStat *ms; + + if( file == NULL ) file = "unknown"; + + if( mw == NULL ) { + mwWrite("relink: cannot repair MW at NULL\n"); + FLUSH(); + goto emergency; + } + + if( !mwIsSafeAddr(mw, mwDataSize) ) { + mwWrite("relink: MW-%p is a garbage pointer\n", mw); + FLUSH(); + goto emergency; + } + + mwWrite("relink: <%ld> %s(%d) attempting to repair MW-%p...\n", mwCounter, file, line, mw ); + FLUSH(); + fails = 0; + + /* Repair from head */ + if( mwHead != mw ) { + if( !mwIsSafeAddr( mwHead, mwDataSize ) ) { + mwWrite("relink: failed for MW-%p; head pointer destroyed\n", mw ); + FLUSH(); + goto emergency; + } + for( mw1=mwHead; mw1; mw1=mw1->next ) { + if( mw1->next == mw ) { + mw->prev = mw1; + break; + } + if( mw1->next && + ( !mwIsSafeAddr(mw1->next, mwDataSize ) || mw1->next->prev != mw1) ) { + mwWrite("relink: failed for MW-%p; forward chain fragmented at MW-%p: 'next' is %p\n", mw, mw1, mw1->next ); + FLUSH(); + goto emergency; + } + } + if( mw1 == NULL ) { + mwWrite("relink: MW-%p not found in forward chain search\n", mw ); + FLUSH(); + fails ++; + } + } + else + { + mwWrite( "relink: MW-%p is the head (first) allocation\n", mw ); + if( mw->prev != NULL ) + { + mwWrite( "relink: MW-%p prev pointer is non-NULL, you have a wild pointer\n", mw ); + mw->prev = NULL; + } + } + + /* Repair from tail */ + if( mwTail != mw ) { + if( !mwIsSafeAddr( mwTail, mwDataSize ) ) { + mwWrite("relink: failed for MW-%p; tail pointer destroyed\n", mw ); + FLUSH(); + goto emergency; + } + for( mw1=mwTail; mw1; mw1=mw1->prev ) { + if( mw1->prev == mw ) { + mw->next = mw1; + break; + } + if( mw1->prev && (!mwIsSafeAddr(mw1->prev, mwDataSize ) || mw1->prev->next != mw1) ) { + mwWrite("relink: failed for MW-%p; reverse chain fragmented at MW-%p, 'prev' is %p\n", mw, mw1, mw1->prev ); + FLUSH(); + goto emergency; + } + } + if( mw1 == NULL ) { + mwWrite("relink: MW-%p not found in reverse chain search\n", mw ); + FLUSH(); + fails ++; + } + } + else + { + mwWrite( "relink: MW-%p is the tail (last) allocation\n", mw ); + if( mw->next != NULL ) + { + mwWrite( "relink: MW-%p next pointer is non-NULL, you have a wild pointer\n", mw ); + mw->next = NULL; + } + } + + if( fails > 1 ) { + mwWrite("relink: heap appears intact, MW-%p probably garbage pointer\n", mw ); + FLUSH(); + goto verifyok; + } + + /* restore MW info where possible */ + if( mwIsReadAddr( mw->file, 1 ) ) { + ms = mwStatGet( mw->file, -1, 0 ); + if( ms == NULL ) mw->file = ""; + } + mw->check = CHKVAL(mw); + goto verifyok; + + /* Emergency repair */ + emergency: + + if( mwHead == NULL && mwTail == NULL ) + { + if( mwStatCurAlloc == 0 ) + mwWrite("relink: <%ld> %s(%d) heap is empty, nothing to repair\n", mwCounter, file, line ); + else + mwWrite("relink: <%ld> %s(%d) heap damaged beyond repair\n", mwCounter, file, line ); + FLUSH(); + return 0; + } + + mwWrite("relink: <%ld> %s(%d) attempting emergency repairs...\n", mwCounter, file, line ); + FLUSH(); + + if( mwHead == NULL || mwTail == NULL ) + { + if( mwHead == NULL ) mwWrite("relink: mwHead is NULL, but mwTail is %p\n", mwTail ); + else mwWrite("relink: mwTail is NULL, but mwHead is %p\n", mwHead ); + } + + mw1=NULL; + if( mwHead != NULL ) + { + if( !mwIsReadAddr( mwHead, mwDataSize ) || mwHead->check != CHKVAL(mwHead) ) + { + mwWrite("relink: mwHead (MW-%p) is damaged, skipping forward scan\n", mwHead ); + mwHead = NULL; + goto scan_reverse; + } + if( mwHead->prev != NULL ) + { + mwWrite("relink: the mwHead pointer's 'prev' member is %p, not NULL\n", mwHead->prev ); + } + for( mw1=mwHead; mw1; mw1=mw1->next ) + { + if( mw1->next ) + { + if( !mwIsReadAddr(mw1->next,mwDataSize) || + (!mw1->next->check) != CHKVAL(mw1) || + mw1->next->prev != mw1 ) + { + mwWrite("relink: forward chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n", + mw1, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw1->file, mw1->line ); + if( mwIsReadAddr(mw1->next,mwDataSize ) ) + { + mwWrite("relink: forward chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n", + mw1->next, mw1->size, (mw->flag & MW_NML)?"NoMansLand ":"", + mwIsReadAddr(mw1->file,16)?mw1->file:"", mw1->line ); + } + else + { + mwWrite("relink: the 'next' pointer of this MW points to %p, which is out-of-legal-access\n", + mw1->next ); + } + break; + } + } + } + } + + +scan_reverse: + mw2=NULL; + if( mwTail != NULL ) + { + if( !mwIsReadAddr(mwTail,mwDataSize) || mwTail->check != CHKVAL(mwTail) ) + { + mwWrite("relink: mwTail (%p) is damaged, skipping reverse scan\n", mwTail ); + mwTail = NULL; + goto analyze; + } + if( mwTail->next != NULL ) + { + mwWrite("relink: the mwTail pointer's 'next' member is %p, not NULL\n", mwTail->next ); + } + for( mw2=mwTail; mw2; mw2=mw2->prev ) + { + if( mw2->prev ) + { + if( !mwIsReadAddr(mw2->prev,mwDataSize) || + (!mw2->prev->check) != CHKVAL(mw2) || + mw2->prev->next != mw2 ) + { + mwWrite("relink: reverse chain's last intact MW is MW-%p, %ld %sbytes at %s(%d)\n", + mw2, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"", mw2->file, mw2->line ); + if( mwIsReadAddr(mw2->prev,mwDataSize ) ) + { + mwWrite("relink: reverse chain's first damaged MW is MW-%p, %ld %sbytes at %s(%d)\n", + mw2->prev, mw2->size, (mw->flag & MW_NML)?"NoMansLand ":"", + mwIsReadAddr(mw2->file,16)?mw2->file:"", mw2->line ); + } + else + { + mwWrite("relink: the 'prev' pointer of this MW points to %p, which is out-of-legal-access\n", + mw2->prev ); + } + break; + } + } + } + } + +analyze: + if( mwHead == NULL && mwTail == NULL ) + { + mwWrite("relink: both head and tail pointers damaged, aborting program\n"); + mwFlushW(1); + FLUSH(); + abort(); + } + if( mwHead == NULL ) + { + mwHead = mw2; + mwWrite("relink: heap truncated, MW-%p designated as new mwHead\n", mw2 ); + mw2->prev = NULL; + mw1 = mw2 = NULL; + } + if( mwTail == NULL ) + { + mwTail = mw1; + mwWrite("relink: heap truncated, MW-%p designated as new mwTail\n", mw1 ); + mw1->next = NULL; + mw1 = mw2 = NULL; + } + if( mw1 == NULL && mw2 == NULL && + mwHead->prev == NULL && mwTail->next == NULL ) { + mwWrite("relink: verifying heap integrity...\n" ); + FLUSH(); + goto verifyok; + } + if( mw1 && mw2 && mw1 != mw2 ) { + mw1->next = mw2; + mw2->prev = mw1; + mwWrite("relink: emergency repairs successful, assessing damage...\n"); + FLUSH(); + } + else { + mwWrite("relink: heap totally destroyed, aborting program\n"); + mwFlushW(1); + FLUSH(); + abort(); + } + + /* Verify by checking that the number of active allocations */ + /* match the number of entries in the chain */ +verifyok: + if( !mwIsHeapOK( NULL ) ) { + mwWrite("relink: heap verification FAILS - aborting program\n"); + mwFlushW(1); + FLUSH(); + abort(); + } + for( size=count=0, mw1=mwHead; mw1; mw1=mw1->next ) { + count ++; + size += (long) mw1->size; + } + if( count == mwNumCurAlloc ) { + mwWrite("relink: successful, "); + if( size == mwStatCurAlloc ) { + mwWrite("no allocations lost\n"); + } + else { + if( mw != NULL ) { + mwWrite("size information lost for MW-%p\n", mw); + mw->size = 0; + } + } + } + else { + mwWrite("relink: partial, %ld MW-blocks of %ld bytes lost\n", + mwNmlNumAlloc+mwNumCurAlloc-count, mwNmlCurAlloc+mwStatCurAlloc-size ); + return 0; + } + + return 1; + } + +/* +** If mwData* is NULL: +** Returns 0 if heap chain is broken. +** Returns 1 if heap chain is intact. +** If mwData* is not NULL: +** Returns 0 if mwData* is missing or if chain is broken. +** Returns 1 if chain is intact and mwData* is found. +*/ +static int mwIsHeapOK( mwData *includes_mw ) { + int found = 0; + mwData *mw; + + for( mw = mwHead; mw; mw=mw->next ) { + if( includes_mw == mw ) found++; + if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0; + if( mw->prev ) { + if( !mwIsSafeAddr( mw->prev, mwDataSize ) ) return 0; + if( mw==mwHead || mw->prev->next != mw ) return 0; + } + if( mw->next ) { + if( !mwIsSafeAddr( mw->next, mwDataSize ) ) return 0; + if( mw==mwTail || mw->next->prev != mw ) return 0; + } + else if( mw!=mwTail ) return 0; + } + + if( includes_mw != NULL && !found ) return 0; + + return 1; + } + +static int mwIsOwned( mwData* mw, const char *file, int line ) { + int retv; + mwStat *ms; + + /* see if the address is legal according to OS */ + if( !mwIsSafeAddr( mw, mwDataSize ) ) return 0; + + /* make sure we have _anything_ allocated */ + if( mwHead == NULL && mwTail == NULL && mwStatCurAlloc == 0 ) + return 0; + + /* calculate checksum */ + if( mw->check != CHKVAL(mw) ) { + /* may be damaged checksum, see if block is in heap */ + if( mwIsHeapOK( mw ) ) { + /* damaged checksum, repair it */ + mwWrite( "internal: <%ld> %s(%d), checksum for MW-%p is incorrect\n", + mwCounter, file, line, mw ); + mwIncErr(); + if( mwIsReadAddr( mw->file, 1 ) ) { + ms = mwStatGet( mw->file, -1, 0 ); + if( ms == NULL ) mw->file = ""; + } + else mw->file = ""; + mw->size = 0; + mw->check = CHKVAL(mw); + return 1; + } + /* no, it's just some garbage data */ + return 0; + } + + /* check that the non-NULL pointers are safe */ + if( mw->prev && !mwIsSafeAddr( mw->prev, mwDataSize ) ) mwRelink( mw, file, line ); + if( mw->next && !mwIsSafeAddr( mw->next, mwDataSize ) ) mwRelink( mw, file, line ); + + /* safe address, checksum OK, proceed with heap checks */ + + /* see if the block is in the heap */ + retv = 0; + if( mw->prev ) { if( mw->prev->next == mw ) retv ++; } + else { if( mwHead == mw ) retv++; } + if( mw->next ) { if( mw->next->prev == mw ) retv ++; } + else { if( mwTail == mw ) retv++; } + if( mw->check == CHKVAL(mw) ) retv ++; + if( retv > 2 ) return 1; + + /* block not in heap, check heap for corruption */ + + if( !mwIsHeapOK( mw ) ) { + if( mwRelink( mw, file, line ) ) + return 1; + } + + /* unable to repair */ + mwWrite( "internal: <%ld> %s(%d), mwIsOwned fails for MW-%p\n", + mwCounter, file, line, mw ); + mwIncErr(); + + return 0; + } + +/* +** mwTestBuf: +** Checks a buffers links and pre/postfixes. +** Writes errors found to the log. +** Returns zero if no errors found. +*/ +static int mwTestBuf( mwData* mw, const char* file, int line ) { + int retv = 0; + char *p; + + if( file == NULL ) file = "unknown"; + + if( !mwIsSafeAddr( mw, mwDataSize + mwOverflowZoneSize ) ) { + mwWrite( "internal: <%ld> %s(%d): pointer MW-%p is invalid\n", + mwCounter, file, line, mw ); + mwIncErr(); + return 2; + } + + if( mw->check != CHKVAL(mw) ) { + mwWrite( "internal: <%ld> %s(%d), info trashed; relinking\n", + mwCounter, file, line ); + mwIncErr(); + if( !mwRelink( mw, file, line ) ) return 2; + } + + if( mw->prev && mw->prev->next != mw ) { + mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link1 broken\n", + mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line ); + mwIncErr(); + if( !mwRelink( mw, file, line ) ) retv = 2; + } + if( mw->next && mw->next->prev != mw ) { + mwWrite( "internal: <%ld> %s(%d), buffer <%ld> %s(%d) link2 broken\n", + mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line ); + mwIncErr(); + if( !mwRelink( mw, file, line ) ) retv = 2; + } + + p = ((char*)mw) + mwDataSize; + if( mwCheckOF( p ) ) { + mwWrite( "underflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n", + mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line ); + mwIncErr(); + retv = 1; + } + p += mwOverflowZoneSize + mw->size; + if( mwIsReadAddr( p, mwOverflowZoneSize ) && mwCheckOF( p ) ) { + mwWrite( "overflow: <%ld> %s(%d), %ld bytes alloc'd at <%ld> %s(%d)\n", + mwCounter,file,line, (long)mw->size, mw->count, mw->file, mw->line ); + mwIncErr(); + retv = 1; + } + + return retv; + } + +static void mwDefaultOutFunc( int c ) { + if( mwLogR() ) fputc( c, mwLogR() ); + } + +static void mwWrite( const char *format, ... ) { + int tot, oflow = 0; + va_list mark; + mwAutoInit(); + if( mwOutFunction == NULL ) mwOutFunction = mwDefaultOutFunc; + va_start( mark, format ); + tot = vsprintf( mwPrintBuf, format, mark ); + va_end( mark ); + if( tot >= MW_TRACE_BUFFER ) { mwPrintBuf[MW_TRACE_BUFFER] = 0; oflow = 1; } + for(tot=0;mwPrintBuf[tot];tot++) + (*mwOutFunction)( mwPrintBuf[tot] ); + if( oflow ) { + mwWrite( "\ninternal: mwWrite(): WARNING! OUTPUT EXCEEDED %u CHARS: SYSTEM UNSTABLE\n", MW_TRACE_BUFFER-1 ); + FLUSH(); + } + return; + } + +static void mwLogFile( const char *name ) { + time_t tid; + (void) time( &tid ); + if( mwLogR() != NULL ) { + fclose( mwLogR() ); + mwLogW( NULL ); + } + if( name == NULL ) return; + mwLogW( fopen( name, "a" COMMIT ) ); + if( mwLogR() == NULL ) + mwWrite( "logfile: failed to open/create file '%s'\n", name ); + } + +/* +** Try to free NML memory until a contiguous allocation of +** 'needed' bytes can be satisfied. If this is not enough +** and the 'urgent' parameter is nonzero, grabbed memory is +** also freed. +*/ +static size_t mwFreeUp( size_t needed, int urgent ) { + void *p; + mwData *mw, *mw2; + char *data; + + /* free grabbed NML memory */ + for(;;) { + if( mwDrop_( 1, MW_VAL_NML, 1 ) == 0 ) break; + p = malloc( needed ); + if( p == NULL ) continue; + free( p ); + return needed; + } + + /* free normal NML memory */ + mw = mwHead; + while( mw != NULL ) { + if( !(mw->flag & MW_NML) ) mw = mw->next; + else { + data = ((char*)mw)+mwDataSize+mwOverflowZoneSize; + if( mwTestMem( data, mw->size, MW_VAL_NML ) ) { + mwIncErr(); + mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n", + mw->count, data + mwOverflowZoneSize, mw->file, mw->line ); + } + mw2 = mw->next; + mwUnlink( mw, "mwFreeUp", 0 ); + free( mw ); + mw = mw2; + p = malloc( needed ); + if( p == NULL ) continue; + free( p ); + return needed; + } + } + + /* if not urgent (for internal purposes), fail */ + if( !urgent ) return 0; + + /* free grabbed memory */ + for(;;) { + if( mwDrop_( 1, MW_VAL_GRB, 1 ) == 0 ) break; + p = malloc( needed ); + if( p == NULL ) continue; + free( p ); + return needed; + } + + return 0; + } + +static const void * mwTestMem( const void *p, unsigned len, int c ) { + const unsigned char *ptr; + ptr = (const unsigned char *) p; + while( len-- ) { + if( *ptr != (unsigned char)c ) return (const void*)ptr; + ptr ++; + } + return NULL; + } + +static int mwStrCmpI( const char *s1, const char *s2 ) { + if( s1 == NULL || s2 == NULL ) return 0; + while( *s1 ) { + if( toupper(*s2) == toupper(*s1) ) { s1++; s2++; continue; } + return 1; + } + return 0; + } + +#define AIPH() if( always_invoked ) { mwWrite("autocheck: <%ld> %s(%d) ", mwCounter, file, line ); always_invoked = 0; } + +static int mwTestNow( const char *file, int line, int always_invoked ) { + int retv = 0; + mwData *mw; + char *data; + + if( file && !always_invoked ) + mwWrite("check: <%ld> %s(%d), checking %s%s%s\n", + mwCounter, file, line, + (mwTestFlags & MW_TEST_CHAIN) ? "chain ": "", + (mwTestFlags & MW_TEST_ALLOC) ? "alloc ": "", + (mwTestFlags & MW_TEST_NML) ? "nomansland ": "" + ); + + if( mwTestFlags & MW_TEST_CHAIN ) { + for( mw = mwHead; mw; mw=mw->next ) { + if( !mwIsSafeAddr(mw, mwDataSize) ) { + AIPH(); + mwWrite("check: heap corruption detected\n"); + mwIncErr(); + return retv + 1; + } + if( mw->prev ) { + if( !mwIsSafeAddr(mw->prev, mwDataSize) ) { + AIPH(); + mwWrite("check: heap corruption detected\n"); + mwIncErr(); + return retv + 1; + } + if( mw==mwHead || mw->prev->next != mw ) { + AIPH(); + mwWrite("check: heap chain broken, prev link incorrect\n"); + mwIncErr(); + retv ++; + } + } + if( mw->next ) { + if( !mwIsSafeAddr(mw->next, mwDataSize) ) { + AIPH(); + mwWrite("check: heap corruption detected\n"); + mwIncErr(); + return retv + 1; + } + if( mw==mwTail || mw->next->prev != mw ) { + AIPH(); + mwWrite("check: heap chain broken, next link incorrect\n"); + mwIncErr(); + retv ++; + } + } + else if( mw!=mwTail ) { + AIPH(); + mwWrite("check: heap chain broken, tail incorrect\n"); + mwIncErr(); + retv ++; + } + } + } + if( mwTestFlags & MW_TEST_ALLOC ) { + for( mw = mwHead; mw; mw=mw->next ) { + if( mwTestBuf( mw, file, line ) ) retv ++; + } + } + if( mwTestFlags & MW_TEST_NML ) { + for( mw = mwHead; mw; mw=mw->next ) { + if( (mw->flag & MW_NML) ) { + data = ((char*)mw)+mwDataSize+mwOverflowZoneSize; + if( mwTestMem( data, mw->size, MW_VAL_NML ) ) { + mwIncErr(); + mwWrite( "wild pointer: <%ld> NoMansLand %p alloc'd at %s(%d)\n", + mw->count, data + mwOverflowZoneSize, mw->file, mw->line ); + } + } + } + } + + + if( file && !always_invoked && !retv ) + mwWrite("check: <%ld> %s(%d), complete; no errors\n", + mwCounter, file, line ); + return retv; + } + +/********************************************************************** +** Statistics +**********************************************************************/ + +static void mwStatReport() +{ + mwStat* ms, *ms2; + const char *modname; + int modnamelen; + + /* global statistics report */ + mwWrite( "\nMemory usage statistics (global):\n" ); + mwWrite( " N)umber of allocations made: %ld\n", mwStatNumAlloc ); + mwWrite( " L)argest memory usage : %ld\n", mwStatMaxAlloc ); + mwWrite( " T)otal of all alloc() calls: %ld\n", mwStatTotAlloc ); + mwWrite( " U)nfreed bytes totals : %ld\n", mwStatCurAlloc ); + FLUSH(); + + if( mwStatLevel < 1 ) return; + + /* on a per-module basis */ + mwWrite( "\nMemory usage statistics (detailed):\n"); + mwWrite( " Module/Line Number Largest Total Unfreed \n"); + for( ms=mwStatList; ms; ms=ms->next ) + { + if( ms->line == -1 ) + { + if( ms->file == NULL || !mwIsReadAddr(ms->file,22) ) modname = ""; + else modname = ms->file; + modnamelen = strlen(modname); + if( modnamelen > 42 ) + { + modname = modname + modnamelen - 42; + } + + mwWrite(" %-42s %-8ld %-8ld %-8ld %-8ld\n", + modname, ms->num, ms->max, ms->total, ms->curr ); + if( ms->file && mwStatLevel > 1 ) + { + for( ms2=mwStatList; ms2; ms2=ms2->next ) + { + if( ms2->line!=-1 && ms2->file!=NULL && !mwStrCmpI( ms2->file, ms->file ) ) + { + mwWrite( " %-8d %-8ld %-8ld %-8ld %-8ld\n", + ms2->line, ms2->num, ms2->max, ms2->total, ms2->curr ); + } + } + } + } + } +} + +static mwStat* mwStatGet( const char *file, int line, int makenew ) { + mwStat* ms; + + if( mwStatLevel < 2 ) line = -1; + + for( ms=mwStatList; ms!=NULL; ms=ms->next ) { + if( line != ms->line ) continue; + if( file==NULL ) { + if( ms->file == NULL ) break; + continue; + } + if( ms->file == NULL ) continue; + if( !strcmp( ms->file, file ) ) break; + } + + if( ms != NULL ) return ms; + + if( !makenew ) return NULL; + + ms = (mwStat*) malloc( sizeof(mwStat) ); + if( ms == NULL ) { + if( mwFreeUp( sizeof(mwStat), 0 ) < sizeof(mwStat) || + (ms=(mwStat*)malloc(sizeof(mwStat))) == NULL ) { + mwWrite("internal: memory low, statistics incomplete for '%s'\n", file ); + return NULL; + } + } + ms->file = file; + ms->line = line; + ms->total = 0L; + ms->max = 0L; + ms->num = 0L; + ms->curr = 0L; + ms->next = mwStatList; + mwStatList = ms; + return ms; + } + +static void mwStatAlloc( size_t size, const char* file, int line ) { + mwStat* ms; + + /* update the module statistics */ + ms = mwStatGet( file, -1, 1 ); + if( ms != NULL ) { + ms->total += (long) size; + ms->curr += (long) size; + ms->num ++; + if( ms->curr > ms->max ) ms->max = ms->curr; + } + + /* update the line statistics */ + if( mwStatLevel > 1 && line != -1 && file ) { + ms = mwStatGet( file, line, 1 ); + if( ms != NULL ) { + ms->total += (long) size; + ms->curr += (long) size; + ms->num ++; + if( ms->curr > ms->max ) ms->max = ms->curr; + } + } + + } + +static void mwStatFree( size_t size, const char* file, int line ) { + mwStat* ms; + + /* update the module statistics */ + ms = mwStatGet( file, -1, 1 ); + if( ms != NULL ) ms->curr -= (long) size; + + /* update the line statistics */ + if( mwStatLevel > 1 && line != -1 && file ) { + ms = mwStatGet( file, line, 1 ); + if( ms != NULL ) ms->curr -= (long) size; + } + } + +/*********************************************************************** +** Safe memory checkers +** +** Using ifdefs, implement the operating-system specific mechanism +** of identifying a piece of memory as legal to access with read +** and write priviliges. Default: return nonzero for non-NULL pointers. +***********************************************************************/ + +static char mwDummy( char c ) +{ + return c; +} + +#ifndef MW_SAFEADDR +#ifdef WIN32 +#define MW_SAFEADDR +#define WIN32_LEAN_AND_MEAN +#include +int mwIsReadAddr( const void *p, unsigned len ) +{ + if( p == NULL ) return 0; + if( IsBadReadPtr(p,len) ) return 0; + return 1; +} +int mwIsSafeAddr( void *p, unsigned len ) +{ + /* NOTE: For some reason, under Win95 the IsBad... */ + /* can return false for invalid pointers. */ + if( p == NULL ) return 0; + if( IsBadReadPtr(p,len) || IsBadWritePtr(p,len) ) return 0; + return 1; +} +#endif /* WIN32 */ +#endif /* MW_SAFEADDR */ + +#ifndef MW_SAFEADDR +#ifdef SIGSEGV +#define MW_SAFEADDR + +typedef void (*mwSignalHandlerPtr)( int ); +mwSignalHandlerPtr mwOldSIGSEGV = (mwSignalHandlerPtr) 0; +jmp_buf mwSIGSEGVjump; +static void mwSIGSEGV( int n ); + +static void mwSIGSEGV( int n ) +{ + n = n; + longjmp( mwSIGSEGVjump, 1 ); +} + +int mwIsReadAddr( const void *p, unsigned len ) +{ + const char *ptr; + + if( p == NULL ) return 0; + if( !len ) return 1; + + /* set up to catch the SIGSEGV signal */ + mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV ); + + if( setjmp( mwSIGSEGVjump ) ) + { + signal( SIGSEGV, mwOldSIGSEGV ); + return 0; + } + + /* read all the bytes in the range */ + ptr = (const char *)p; + ptr += len; + + /* the reason for this rather strange construct is that */ + /* we want to keep the number of used parameters and locals */ + /* to a minimum. if we use len for a counter gcc will complain */ + /* it may get clobbered by longjmp() at high warning levels. */ + /* it's a harmless warning, but this way we don't have to see it. */ + do + { + ptr --; + if( *ptr == 0x7C ) (void) mwDummy( (char)0 ); + } while( (const void*) ptr != p ); + + /* remove the handler */ + signal( SIGSEGV, mwOldSIGSEGV ); + + return 1; +} +int mwIsSafeAddr( void *p, unsigned len ) +{ + char *ptr; + + if( p == NULL ) return 0; + if( !len ) return 1; + + /* set up to catch the SIGSEGV signal */ + mwOldSIGSEGV = signal( SIGSEGV, mwSIGSEGV ); + + if( setjmp( mwSIGSEGVjump ) ) + { + signal( SIGSEGV, mwOldSIGSEGV ); + return 0; + } + + /* read and write-back all the bytes in the range */ + ptr = (char *)p; + ptr += len; + + /* the reason for this rather strange construct is that */ + /* we want to keep the number of used parameters and locals */ + /* to a minimum. if we use len for a counter gcc will complain */ + /* it may get clobbered by longjmp() at high warning levels. */ + /* it's a harmless warning, but this way we don't have to see it. */ + do + { + ptr --; + *ptr = mwDummy( *ptr ); + } while( (void*) ptr != p ); + + /* remove the handler */ + signal( SIGSEGV, mwOldSIGSEGV ); + + return 1; +} +#endif /* SIGSEGV */ +#endif /* MW_SAFEADDR */ + +#ifndef MW_SAFEADDR +int mwIsReadAddr( const void *p, unsigned len ) +{ + if( p == NULL ) return 0; + if( len == 0 ) return 1; + return 1; +} +int mwIsSafeAddr( void *p, unsigned len ) +{ + if( p == NULL ) return 0; + if( len == 0 ) return 1; + return 1; +} +#endif + +/********************************************************************** +** Mutex handling +**********************************************************************/ + +#if defined(WIN32) || defined(__WIN32__) + +static void mwMutexInit( void ) +{ + mwGlobalMutex = CreateMutex( NULL, FALSE, NULL); + return; +} + +static void mwMutexTerm( void ) +{ + CloseHandle( mwGlobalMutex ); + return; +} + +static void mwMutexLock( void ) +{ + if( WaitForSingleObject(mwGlobalMutex, 1000 ) == WAIT_TIMEOUT ) + { + mwWrite( "mwMutexLock: timed out, possible deadlock\n" ); + } + return; +} + +static void mwMutexUnlock( void ) +{ + ReleaseMutex( mwGlobalMutex ); + return; +} + +#endif + +#if defined(MW_PTHREADS) || defined(HAVE_PTHREAD_H) + +static void mwMutexInit( void ) +{ + pthread_mutex_init( &mwGlobalMutex, NULL ); + return; +} + +static void mwMutexTerm( void ) +{ + pthread_mutex_destroy( &mwGlobalMutex ); + return; +} + +static void mwMutexLock( void ) +{ + pthread_mutex_lock(&mwGlobalMutex); + return; +} + +static void mwMutexUnlock( void ) +{ + pthread_mutex_unlock(&mwGlobalMutex); + return; +} + +#endif + +/********************************************************************** +** C++ new & delete +**********************************************************************/ + +#if 0 /* 980317: disabled C++ */ + +#ifdef __cplusplus +#ifndef MEMWATCH_NOCPP + +int mwNCur = 0; +const char *mwNFile = NULL; +int mwNLine = 0; + +class MemWatch { +public: + MemWatch(); + ~MemWatch(); + }; + +MemWatch::MemWatch() { + if( mwInited ) return; + mwUseAtexit = 0; + mwInit(); + } + +MemWatch::~MemWatch() { + if( mwUseAtexit ) return; + mwTerm(); + } + +/* +** This global new will catch all 'new' calls where MEMWATCH is +** not active. +*/ +void* operator new( unsigned size ) { + mwNCur = 0; + return mwMalloc( size, "", 0 ); + } + +/* +** This is the new operator that's called when a module uses mwNew. +*/ +void* operator new( unsigned size, const char *file, int line ) { + mwNCur = 0; + return mwMalloc( size, file, line ); + } + +/* +** This is the new operator that's called when a module uses mwNew[]. +** -- hjc 07/16/02 +*/ +void* operator new[] ( unsigned size, const char *file, int line ) { + mwNCur = 0; + return mwMalloc( size, file, line ); + } + +/* +** Since this delete operator will recieve ALL delete's +** even those from within libraries, we must accept +** delete's before we've been initialized. Nor can we +** reliably check for wild free's if the mwNCur variable +** is not set. +*/ +void operator delete( void *p ) { + if( p == NULL ) return; + if( !mwInited ) { + free( p ); + return; + } + if( mwNCur ) { + mwFree( p, mwNFile, mwNLine ); + mwNCur = 0; + return; + } + mwFree_( p ); + } + +void operator delete[]( void *p ) { + if( p == NULL ) return; + if( !mwInited ) { + free( p ); + return; + } + if( mwNCur ) { + mwFree( p, mwNFile, mwNLine ); + mwNCur = 0; + return; + } + mwFree_( p ); + } + +#endif /* MEMWATCH_NOCPP */ +#endif /* __cplusplus */ + +#endif /* 980317: disabled C++ */ + +/* MEMWATCH.C */ diff --git a/client/src/thirdparty/memwatch/memwatch.h b/client/src/thirdparty/memwatch/memwatch.h new file mode 100644 index 0000000..d63fd76 --- /dev/null +++ b/client/src/thirdparty/memwatch/memwatch.h @@ -0,0 +1,707 @@ +/* +** MEMWATCH.H +** Nonintrusive ANSI C memory leak / overwrite detection +** Copyright (C) 1992-2002 Johan Lindh +** All rights reserved. +** Version 2.71 +** +************************************************************************ +** +** PURPOSE: +** +** MEMWATCH has been written to allow guys and gals that like to +** program in C a public-domain memory error control product. +** I hope you'll find it's as advanced as most commercial packages. +** The idea is that you use it during the development phase and +** then remove the MEMWATCH define to produce your final product. +** MEMWATCH is distributed in source code form in order to allow +** you to compile it for your platform with your own compiler. +** It's aim is to be 100% ANSI C, but some compilers are more stingy +** than others. If it doesn't compile without warnings, please mail +** me the configuration of operating system and compiler you are using +** along with a description of how to modify the source, and the version +** number of MEMWATCH that you are using. +** +************************************************************************ + + This file is part of MEMWATCH. + + MEMWATCH is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + MEMWATCH is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with MEMWATCH; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +************************************************************************ +** +** REVISION HISTORY: +** +** 920810 JLI [1.00] +** 920830 JLI [1.10 double-free detection] +** 920912 JLI [1.15 mwPuts, mwGrab/Drop, mwLimit] +** 921022 JLI [1.20 ASSERT and VERIFY] +** 921105 JLI [1.30 C++ support and TRACE] +** 921116 JLI [1.40 mwSetOutFunc] +** 930215 JLI [1.50 modified ASSERT/VERIFY] +** 930327 JLI [1.51 better auto-init & PC-lint support] +** 930506 JLI [1.55 MemWatch class, improved C++ support] +** 930507 JLI [1.60 mwTest & CHECK()] +** 930809 JLI [1.65 Abort/Retry/Ignore] +** 930820 JLI [1.70 data dump when unfreed] +** 931016 JLI [1.72 modified C++ new/delete handling] +** 931108 JLI [1.77 mwSetAssertAction() & some small changes] +** 940110 JLI [1.80 no-mans-land alloc/checking] +** 940328 JLI [2.00 version 2.0 rewrite] +** Improved NML (no-mans-land) support. +** Improved performance (especially for free()ing!). +** Support for 'read-only' buffers (checksums) +** ^^ NOTE: I never did this... maybe I should? +** FBI (free'd block info) tagged before freed blocks +** Exporting of the mwCounter variable +** mwBreakOut() localizes debugger support +** Allocation statistics (global, per-module, per-line) +** Self-repair ability with relinking +** 950913 JLI [2.10 improved garbage handling] +** 951201 JLI [2.11 improved auto-free in emergencies] +** 960125 JLI [X.01 implemented auto-checking using mwAutoCheck()] +** 960514 JLI [2.12 undefining of existing macros] +** 960515 JLI [2.13 possibility to use default new() & delete()] +** 960516 JLI [2.20 suppression of file flushing on unfreed msgs] +** 960516 JLI [2.21 better support for using MEMWATCH with DLL's] +** 960710 JLI [X.02 multiple logs and mwFlushNow()] +** 960801 JLI [2.22 merged X.01 version with current] +** 960805 JLI [2.30 mwIsXXXXAddr() to avoid unneeded GP's] +** 960805 JLI [2.31 merged X.02 version with current] +** 961002 JLI [2.32 support for realloc() + fixed STDERR bug] +** 961222 JLI [2.40 added mwMark() & mwUnmark()] +** 970101 JLI [2.41 added over/underflow checking after failed ASSERT/VERIFY] +** 970113 JLI [2.42 added support for PC-Lint 7.00g] +** 970207 JLI [2.43 added support for strdup()] +** 970209 JLI [2.44 changed default filename to lowercase] +** 970405 JLI [2.45 fixed bug related with atexit() and some C++ compilers] +** 970723 JLI [2.46 added MW_ARI_NULLREAD flag] +** 970813 JLI [2.47 stabilized marker handling] +** 980317 JLI [2.48 ripped out C++ support; wasn't working good anyway] +** 980318 JLI [2.50 improved self-repair facilities & SIGSEGV support] +** 980417 JLI [2.51 more checks for invalid addresses] +** 980512 JLI [2.52 moved MW_ARI_NULLREAD to occur before aborting] +** 990112 JLI [2.53 added check for empty heap to mwIsOwned] +** 990217 JLI [2.55 improved the emergency repairs diagnostics and NML] +** 990224 JLI [2.56 changed ordering of members in structures] +** 990303 JLI [2.57 first maybe-fixit-for-hpux test] +** 990516 JLI [2.58 added 'static' to the definition of mwAutoInit] +** 990517 JLI [2.59 fixed some high-sensitivity warnings] +** 990610 JLI [2.60 fixed some more high-sensitivity warnings] +** 990715 JLI [2.61 changed TRACE/ASSERT/VERIFY macro names] +** 991001 JLI [2.62 added CHECK_BUFFER() and mwTestBuffer()] +** 991007 JLI [2.63 first shot at a 64-bit compatible version] +** 991009 JLI [2.64 undef's strdup() if defined, mwStrdup made const] +** 000704 JLI [2.65 added some more detection for 64-bits] +** 010502 JLI [2.66 incorporated some user fixes] +** [mwRelink() could print out garbage pointer (thanks mac@phobos.ca)] +** [added array destructor for C++ (thanks rdasilva@connecttel.com)] +** [added mutex support (thanks rdasilva@connecttel.com)] +** 010531 JLI [2.67 fix: mwMutexXXX() was declared even if MW_HAVE_MUTEX was not defined] +** 010619 JLI [2.68 fix: mwRealloc() could leave the mutex locked] +** 020918 JLI [2.69 changed to GPL, added C++ array allocation by Howard Cohen] +** 030212 JLI [2.70 mwMalloc() bug for very large allocations (4GB on 32bits)] +** 030520 JLI [2.71 added ULONG_LONG_MAX as a 64-bit detector (thanks Sami Salonen)] +** +** To use, simply include 'MEMWATCH.H' as a header file, +** and add MEMWATCH.C to your list of files, and define the macro +** 'MEMWATCH'. If this is not defined, MEMWATCH will disable itself. +** +** To call the standard C malloc / realloc / calloc / free; use mwMalloc_(), +** mwCalloc_() and mwFree_(). Note that mwFree_() will correctly +** free both malloc()'d memory as well as mwMalloc()'d. +** +** 980317: C++ support has been disabled. +** The code remains, but is not compiled. +** +** For use with C++, which allows use of inlining in header files +** and class specific new/delete, you must also define 'new' as +** 'mwNew' and 'delete' as 'mwDelete'. Do this *after* you include +** C++ header files from libraries, otherwise you can mess up their +** class definitions. If you don't define these, the C++ allocations +** will not have source file and line number information. Also note, +** most C++ class libraries implement their own C++ memory management, +** and don't allow anyone to override them. MFC belongs to this crew. +** In these cases, the only thing to do is to use MEMWATCH_NOCPP. +** +** You can capture output from MEMWATCH using mwSetOutFunc(). +** Just give it the adress of a "void myOutFunc(int c)" function, +** and all characters to be output will be redirected there. +** +** A failing ASSERT() or VERIFY() will normally always abort your +** program. This can be changed using mwSetAriFunc(). Give it a +** pointer to a "int myAriFunc(const char *)" function. Your function +** must ask the user whether to Abort, Retry or Ignore the trap. +** Return 2 to Abort, 1 to Retry or 0 to Ignore. Beware retry; it +** causes the expression to be evaluated again! MEMWATCH has a +** default ARI handler. It's disabled by default, but you can enable +** it by calling 'mwDefaultAri()'. Note that this will STILL abort +** your program unless you define MEMWATCH_STDIO to allow MEMWATCH +** to use the standard C I/O streams. Also, setting the ARI function +** will cause MEMWATCH *NOT* to write the ARI error to stderr. The +** error string is passed to the ARI function instead, as the +** 'const char *' parameter. +** +** You can disable MEMWATCH's ASSERT/VERIFY and/or TRACE implementations. +** This can be useful if you're using a debug terminal or smart debugger. +** Disable them by defining MW_NOASSERT, MW_NOVERIFY or MW_NOTRACE. +** +** MEMWATCH fills all allocated memory with the byte 0xFE, so if +** you're looking at erroneous data which are all 0xFE:s, the +** data probably was not initialized by you. The exception is +** calloc(), which will fill with zero's. All freed buffers are +** zapped with 0xFD. If this is what you look at, you're using +** data that has been freed. If this is the case, be aware that +** MEMWATCH places a 'free'd block info' structure immediately +** before the freed data. This block contains info about where +** the block was freed. The information is in readable text, +** in the format "FBIfilename(line)", for example: +** "FBI<267>test.c(12)". Using FBI's slows down free(), so it's +** disabled by default. Use mwFreeBufferInfo(1) to enable it. +** +** To aid in tracking down wild pointer writes, MEMWATCH can perform +** no-mans-land allocations. No-mans-land will contain the byte 0xFC. +** MEMWATCH will, when this is enabled, convert recently free'd memory +** into NML allocations. +** +** MEMWATCH protects it's own data buffers with checksums. If you +** get an internal error, it means you're overwriting wildly, +** or using an uninitialized pointer. +** +************************************************************************ +** +** Note when compiling with Microsoft C: +** - MSC ignores fflush() by default. This is overridden, so that +** the disk log will always be current. +** +** This utility has been tested with: +** PC-lint 7.0k, passed as 100% ANSI C compatible +** Microsoft Visual C++ on Win16 and Win32 +** Microsoft C on DOS +** SAS C on an Amiga 500 +** Gnu C on a PC running Red Hat Linux +** ...and using an (to me) unknown compiler on an Atari machine. +** +************************************************************************ +** +** Format of error messages in MEMWATCH.LOG: +** message: filename(linenumber), information +** +** Errors caught by MemWatch, when they are detected, and any +** actions taken besides writing to the log file MEMWATCH.LOG: +** +** Double-freeing: +** A pointer that was recently freed and has not since been +** reused was freed again. The place where the previous free() +** was executed is displayed. +** Detect: delete or free() using the offending pointer. +** Action: The delete or free() is cancelled, execution continues. +** Underflow: +** You have written just ahead of the allocated memory. +** The size and place of the allocation is displayed. +** Detect: delete or free() of the damaged buffer. +** Action: The buffer is freed, but there may be secondary damage. +** Overflow: +** Like underflow, but you've written after the end of the buffer. +** Detect: see Underflow. +** Action: see Underflow. +** WILD free: +** An unrecognized pointer was passed to delete or free(). +** The pointer may have been returned from a library function; +** in that case, use mwFree_() to force free() of it. +** Also, this may be a double-free, but the previous free was +** too long ago, causing MEMWATCH to 'forget' it. +** Detect: delete or free() of the offending pointer. +** Action: The delete or free() is cancelled, execution continues. +** NULL free: +** It's unclear to me whether or not freeing of NULL pointers +** is legal in ANSI C, therefore a warning is written to the log file, +** but the error counter remains the same. This is legal using C++, +** so the warning does not appear with delete. +** Detect: When you free(NULL). +** Action: The free() is cancelled. +** Failed: +** A request to allocate memory failed. If the allocation is +** small, this may be due to memory depletion, but is more likely +** to be memory fragmentation problems. The amount of memory +** allocated so far is displayed also. +** Detect: When you new, malloc(), realloc() or calloc() memory. +** Action: NULL is returned. +** Realloc: +** A request to re-allocate a memory buffer failed for reasons +** other than out-of-memory. The specific reason is shown. +** Detect: When you realloc() +** Action: realloc() is cancelled, NULL is returned +** Limit fail: +** A request to allocate memory failed since it would violate +** the limit set using mwLimit(). mwLimit() is used to stress-test +** your code under simulated low memory conditions. +** Detect: At new, malloc(), realloc() or calloc(). +** Action: NULL is returned. +** Assert trap: +** An ASSERT() failed. The ASSERT() macro works like C's assert() +** macro/function, except that it's interactive. See your C manual. +** Detect: On the ASSERT(). +** Action: Program ends with an advisory message to stderr, OR +** Program writes the ASSERT to the log and continues, OR +** Program asks Abort/Retry/Ignore? and takes that action. +** Verify trap: +** A VERIFY() failed. The VERIFY() macro works like ASSERT(), +** but if MEMWATCH is not defined, it still evaluates the +** expression, but it does not act upon the result. +** Detect: On the VERIFY(). +** Action: Program ends with an advisory message to stderr, OR +** Program writes the VERIFY to the log and continues, OR +** Program asks Abort/Retry/Ignore? and takes that action. +** Wild pointer: +** A no-mans-land buffer has been written into. MEMWATCH can +** allocate and distribute chunks of memory solely for the +** purpose of trying to catch random writes into memory. +** Detect: Always on CHECK(), but can be detected in several places. +** Action: The error is logged, and if an ARI handler is installed, +** it is executed, otherwise, execution continues. +** Unfreed: +** A memory buffer you allocated has not been freed. +** You are informed where it was allocated, and whether any +** over or underflow has occured. MemWatch also displays up to +** 16 bytes of the data, as much as it can, in hex and text. +** Detect: When MemWatch terminates. +** Action: The buffer is freed. +** Check: +** An error was detected during a CHECK() operation. +** The associated pointer is displayed along with +** the file and line where the CHECK() was executed. +** Followed immediately by a normal error message. +** Detect: When you CHECK() +** Action: Depends on the error +** Relink: +** After a MEMWATCH internal control block has been trashed, +** MEMWATCH tries to repair the damage. If successful, program +** execution will continue instead of aborting. Some information +** about the block may be gone permanently, though. +** Detect: N/A +** Action: Relink successful: program continues. +** Relink fails: program aborts. +** Internal: +** An internal error is flagged by MEMWATCH when it's control +** structures have been damaged. You are likely using an uninitialized +** pointer somewhere in your program, or are zapping memory all over. +** The message may give you additional diagnostic information. +** If possible, MEMWATCH will recover and continue execution. +** Detect: Various actions. +** Action: Whatever is needed +** Mark: +** The program terminated without umarking all marked pointers. Marking +** can be used to track resources other than memory. mwMark(pointer,text,...) +** when the resource is allocated, and mwUnmark(pointer) when it's freed. +** The 'text' is displayed for still marked pointers when the program +** ends. +** Detect: When MemWatch terminates. +** Action: The error is logged. +** +** +************************************************************************ +** +** The author may be reached by e-mail at the address below. If you +** mail me about source code changes in MEMWATCH, remember to include +** MW's version number. +** +** Johan Lindh +** johan@linkdata.se +** +** The latest version of MEMWATCH may be downloaded from +** http://www.linkdata.se/ +*/ + +#ifndef __MEMWATCH_H +#define __MEMWATCH_H + +/* Make sure that malloc(), realloc(), calloc() and free() are declared. */ +/*lint -save -e537 */ +#include +/*lint -restore */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* +** Constants used +** All MEMWATCH constants start with the prefix MW_, followed by +** a short mnemonic which indicates where the constant is used, +** followed by a descriptive text about it. +*/ + +#define MW_ARI_NULLREAD 0x10 /* Null read (to start debugger) */ +#define MW_ARI_ABORT 0x04 /* ARI handler says: abort program! */ +#define MW_ARI_RETRY 0x02 /* ARI handler says: retry action! */ +#define MW_ARI_IGNORE 0x01 /* ARI handler says: ignore error! */ + +#define MW_VAL_NEW 0xFE /* value in newly allocated memory */ +#define MW_VAL_DEL 0xFD /* value in newly deleted memory */ +#define MW_VAL_NML 0xFC /* value in no-mans-land */ +#define MW_VAL_GRB 0xFB /* value in grabbed memory */ + +#define MW_TEST_ALL 0xFFFF /* perform all tests */ +#define MW_TEST_CHAIN 0x0001 /* walk the heap chain */ +#define MW_TEST_ALLOC 0x0002 /* test allocations & NML guards */ +#define MW_TEST_NML 0x0004 /* test all-NML areas for modifications */ + +#define MW_NML_NONE 0 /* no NML */ +#define MW_NML_FREE 1 /* turn FREE'd memory into NML */ +#define MW_NML_ALL 2 /* all unused memory is NML */ +#define MW_NML_DEFAULT 0 /* the default NML setting */ + +#define MW_STAT_GLOBAL 0 /* only global statistics collected */ +#define MW_STAT_MODULE 1 /* collect statistics on a module basis */ +#define MW_STAT_LINE 2 /* collect statistics on a line basis */ +#define MW_STAT_DEFAULT 0 /* the default statistics setting */ + +/* +** MemWatch internal constants +** You may change these and recompile MemWatch to change the limits +** of some parameters. Respect the recommended minimums! +*/ +#define MW_TRACE_BUFFER 2048 /* (min 160) size of TRACE()'s output buffer */ +#define MW_FREE_LIST 64 /* (min 4) number of free()'s to track */ + +/* +** Exported variables +** In case you have to remove the 'const' keyword because your compiler +** doesn't support it, be aware that changing the values may cause +** unpredictable behaviour. +** - mwCounter contains the current action count. You can use this to +** place breakpoints using a debugger, if you want. +*/ +#ifndef __MEMWATCH_C +extern const unsigned long mwCounter; +#endif + +/* +** System functions +** Normally, it is not nessecary to call any of these. MEMWATCH will +** automatically initialize itself on the first MEMWATCH function call, +** and set up a call to mwAbort() using atexit(). Some C++ implementations +** run the atexit() chain before the program has terminated, so you +** may have to use mwInit() or the MemWatch C++ class to get good +** behaviour. +** - mwInit() can be called to disable the atexit() usage. If mwInit() +** is called directly, you must call mwTerm() to end MemWatch, or +** mwAbort(). +** - mwTerm() is usually not nessecary to call; but if called, it will +** call mwAbort() if it finds that it is cancelling the 'topmost' +** mwInit() call. +** - mwAbort() cleans up after MEMWATCH, reports unfreed buffers, etc. +*/ +void mwInit( void ); +void mwTerm( void ); +void mwAbort( void ); + +/* +** Setup functions +** These functions control the operation of MEMWATCH's protective features. +** - mwFlushNow() causes MEMWATCH to flush it's buffers. +** - mwDoFlush() controls whether MEMWATCH flushes the disk buffers after +** writes. The default is smart flushing: MEMWATCH will not flush buffers +** explicitly until memory errors are detected. Then, all writes are +** flushed until program end or mwDoFlush(0) is called. +** - mwLimit() sets the allocation limit, an arbitrary limit on how much +** memory your program may allocate in bytes. Used to stress-test app. +** Also, in virtual-memory or multitasking environs, puts a limit on +** how much MW_NML_ALL can eat up. +** - mwGrab() grabs up X kilobytes of memory. Allocates actual memory, +** can be used to stress test app & OS both. +** - mwDrop() drops X kilobytes of grabbed memory. +** - mwNoMansLand() sets the behaviour of the NML logic. See the +** MW_NML_xxx for more information. The default is MW_NML_DEFAULT. +** - mwStatistics() sets the behaviour of the statistics collector. See +** the MW_STAT_xxx defines for more information. Default MW_STAT_DEFAULT. +** - mwFreeBufferInfo() enables or disables the tagging of free'd buffers +** with freeing information. This information is written in text form, +** using sprintf(), so it's pretty slow. Disabled by default. +** - mwAutoCheck() performs a CHECK() operation whenever a MemWatch function +** is used. Slows down performance, of course. +** - mwCalcCheck() calculates checksums for all data buffers. Slow! +** - mwDumpCheck() logs buffers where stored & calc'd checksums differ. Slow!! +** - mwMark() sets a generic marker. Returns the pointer given. +** - mwUnmark() removes a generic marker. If, at the end of execution, some +** markers are still in existence, these will be reported as leakage. +** returns the pointer given. +*/ +void mwFlushNow( void ); +void mwDoFlush( int onoff ); +void mwLimit( long bytes ); +unsigned mwGrab( unsigned kilobytes ); +unsigned mwDrop( unsigned kilobytes ); +void mwNoMansLand( int mw_nml_level ); +void mwStatistics( int level ); +void mwFreeBufferInfo( int onoff ); +void mwAutoCheck( int onoff ); +void mwCalcCheck( void ); +void mwDumpCheck( void ); +void * mwMark( void *p, const char *description, const char *file, unsigned line ); +void * mwUnmark( void *p, const char *file, unsigned line ); + +/* +** Testing/verification/tracing +** All of these macros except VERIFY() evaluates to a null statement +** if MEMWATCH is not defined during compilation. +** - mwIsReadAddr() checks a memory area for read privilige. +** - mwIsSafeAddr() checks a memory area for both read & write privilige. +** This function and mwIsReadAddr() is highly system-specific and +** may not be implemented. If this is the case, they will default +** to returning nonzero for any non-NULL pointer. +** - CHECK() does a complete memory integrity test. Slow! +** - CHECK_THIS() checks only selected components. +** - CHECK_BUFFER() checks the indicated buffer for errors. +** - mwASSERT() or ASSERT() If the expression evaluates to nonzero, execution continues. +** Otherwise, the ARI handler is called, if present. If not present, +** the default ARI action is taken (set with mwSetAriAction()). +** ASSERT() can be disabled by defining MW_NOASSERT. +** - mwVERIFY() or VERIFY() works just like ASSERT(), but when compiling without +** MEMWATCH the macro evaluates to the expression. +** VERIFY() can be disabled by defining MW_NOVERIFY. +** - mwTRACE() or TRACE() writes some text and data to the log. Use like printf(). +** TRACE() can be disabled by defining MW_NOTRACE. +*/ +int mwIsReadAddr( const void *p, unsigned len ); +int mwIsSafeAddr( void *p, unsigned len ); +int mwTest( const char *file, int line, int mw_test_flags ); +int mwTestBuffer( const char *file, int line, void *p ); +int mwAssert( int, const char*, const char*, int ); +int mwVerify( int, const char*, const char*, int ); + +/* +** User I/O functions +** - mwTrace() works like printf(), but dumps output either to the +** function specified with mwSetOutFunc(), or the log file. +** - mwPuts() works like puts(), dumps output like mwTrace(). +** - mwSetOutFunc() allows you to give the adress of a function +** where all user output will go. (exeption: see mwSetAriFunc) +** Specifying NULL will direct output to the log file. +** - mwSetAriFunc() gives MEMWATCH the adress of a function to call +** when an 'Abort, Retry, Ignore' question is called for. The +** actual error message is NOT printed when you've set this adress, +** but instead it is passed as an argument. If you call with NULL +** for an argument, the ARI handler is disabled again. When the +** handler is disabled, MEMWATCH will automatically take the +** action specified by mwSetAriAction(). +** - mwSetAriAction() sets the default ARI return value MEMWATCH should +** use if no ARI handler is specified. Defaults to MW_ARI_ABORT. +** - mwAriHandler() is an ANSI ARI handler you can use if you like. It +** dumps output to stderr, and expects input from stdin. +** - mwBreakOut() is called in certain cases when MEMWATCH feels it would +** be nice to break into a debugger. If you feel like MEMWATCH, place +** an execution breakpoint on this function. +*/ +void mwTrace( const char* format_string, ... ); +void mwPuts( const char* text ); +void mwSetOutFunc( void (*func)(int) ); +void mwSetAriFunc( int (*func)(const char*) ); +void mwSetAriAction( int mw_ari_value ); +int mwAriHandler( const char* cause ); +void mwBreakOut( const char* cause ); + +/* +** Allocation/deallocation functions +** These functions are the ones actually to perform allocations +** when running MEMWATCH, for both C and C++ calls. +** - mwMalloc() debugging allocator +** - mwMalloc_() always resolves to a clean call of malloc() +** - mwRealloc() debugging re-allocator +** - mwRealloc_() always resolves to a clean call of realloc() +** - mwCalloc() debugging allocator, fills with zeros +** - mwCalloc_() always resolves to a clean call of calloc() +** - mwFree() debugging free. Can only free memory which has +** been allocated by MEMWATCH. +** - mwFree_() resolves to a) normal free() or b) debugging free. +** Can free memory allocated by MEMWATCH and malloc() both. +** Does not generate any runtime errors. +*/ +void* mwMalloc( size_t, const char*, int ); +void* mwMalloc_( size_t ); +void* mwRealloc( void *, size_t, const char*, int ); +void* mwRealloc_( void *, size_t ); +void* mwCalloc( size_t, size_t, const char*, int ); +void* mwCalloc_( size_t, size_t ); +void mwFree( void*, const char*, int ); +void mwFree_( void* ); +char* mwStrdup( const char *, const char*, int ); + +/* +** Enable/disable precompiler block +** This block of defines and if(n)defs make sure that references +** to MEMWATCH is completely removed from the code if the MEMWATCH +** manifest constant is not defined. +*/ +#ifndef __MEMWATCH_C +#ifdef MEMWATCH + +#define mwASSERT(exp) while(mwAssert((int)(exp),#exp,__FILE__,__LINE__)) +#ifndef MW_NOASSERT +#ifndef ASSERT +#define ASSERT mwASSERT +#endif /* !ASSERT */ +#endif /* !MW_NOASSERT */ +#define mwVERIFY(exp) while(mwVerify((int)(exp),#exp,__FILE__,__LINE__)) +#ifndef MW_NOVERIFY +#ifndef VERIFY +#define VERIFY mwVERIFY +#endif /* !VERIFY */ +#endif /* !MW_NOVERIFY */ +#define mwTRACE mwTrace +#ifndef MW_NOTRACE +#ifndef TRACE +#define TRACE mwTRACE +#endif /* !TRACE */ +#endif /* !MW_NOTRACE */ + +/* some compilers use a define and not a function */ +/* for strdup(). */ +#ifdef strdup +#undef strdup +#endif + +#define malloc(n) mwMalloc(n,__FILE__,__LINE__) +#define strdup(p) mwStrdup(p,__FILE__,__LINE__) +#define realloc(p,n) mwRealloc(p,n,__FILE__,__LINE__) +#define calloc(n,m) mwCalloc(n,m,__FILE__,__LINE__) +#define free(p) mwFree(p,__FILE__,__LINE__) +#define CHECK() mwTest(__FILE__,__LINE__,MW_TEST_ALL) +#define CHECK_THIS(n) mwTest(__FILE__,__LINE__,n) +#define CHECK_BUFFER(b) mwTestBuffer(__FILE__,__LINE__,b) +#define MARK(p) mwMark(p,#p,__FILE__,__LINE__) +#define UNMARK(p) mwUnmark(p,__FILE__,__LINE__) + +#else /* MEMWATCH */ + +#define mwASSERT(exp) +#ifndef MW_NOASSERT +#ifndef ASSERT +#define ASSERT mwASSERT +#endif /* !ASSERT */ +#endif /* !MW_NOASSERT */ + +#define mwVERIFY(exp) exp +#ifndef MW_NOVERIFY +#ifndef VERIFY +#define VERIFY mwVERIFY +#endif /* !VERIFY */ +#endif /* !MW_NOVERIFY */ + +/*lint -esym(773,mwTRACE) */ +#define mwTRACE /*lint -save -e506 */ 1?(void)0:mwDummyTraceFunction /*lint -restore */ +#ifndef MW_NOTRACE +#ifndef TRACE +/*lint -esym(773,TRACE) */ +#define TRACE mwTRACE +#endif /* !TRACE */ +#endif /* !MW_NOTRACE */ + +extern void mwDummyTraceFunction(const char *,...); +/*lint -save -e652 */ +#define mwDoFlush(n) +#define mwPuts(s) +#define mwInit() +#define mwGrab(n) +#define mwDrop(n) +#define mwLimit(n) +#define mwTest(f,l) +#define mwSetOutFunc(f) +#define mwSetAriFunc(f) +#define mwDefaultAri() +#define mwNomansland() +#define mwStatistics(f) +#define mwMark(p,t,f,n) (p) +#define mwUnmark(p,f,n) (p) +#define mwMalloc(n,f,l) malloc(n) +#define mwStrdup(p,f,l) strdup(p) +#define mwRealloc(p,n,f,l) realloc(p,n) +#define mwCalloc(n,m,f,l) calloc(n,m) +#define mwFree(p) free(p) +#define mwMalloc_(n) malloc(n) +#define mwRealloc_(p,n) realloc(p,n) +#define mwCalloc_(n,m) calloc(n,m) +#define mwFree_(p) free(p) +#define mwAssert(e,es,f,l) +#define mwVerify(e,es,f,l) (e) +#define mwTrace mwDummyTrace +#define mwTestBuffer(f,l,b) (0) +#define CHECK() +#define CHECK_THIS(n) +#define CHECK_BUFFER(b) +#define MARK(p) (p) +#define UNMARK(p) (p) +/*lint -restore */ + +#endif /* MEMWATCH */ +#endif /* !__MEMWATCH_C */ + +#ifdef __cplusplus + } +#endif + +#if 0 /* 980317: disabled C++ */ + +/* +** C++ support section +** Implements the C++ support. Please note that in order to avoid +** messing up library classes, C++ support is disabled by default. +** You must NOT enable it until AFTER the inclusion of all header +** files belonging to code that are not compiled with MEMWATCH, and +** possibly for some that are! The reason for this is that a C++ +** class may implement it's own new() function, and the preprocessor +** would substitute this crucial declaration for MEMWATCH new(). +** You can forcibly deny C++ support by defining MEMWATCH_NOCPP. +** To enble C++ support, you must be compiling C++, MEMWATCH must +** be defined, MEMWATCH_NOCPP must not be defined, and finally, +** you must define 'new' to be 'mwNew', and 'delete' to be 'mwDelete'. +** Unlike C, C++ code can begin executing *way* before main(), for +** example if a global variable is created. For this reason, you can +** declare a global variable of the class 'MemWatch'. If this is +** is the first variable created, it will then check ALL C++ allocations +** and deallocations. Unfortunately, this evaluation order is not +** guaranteed by C++, though the compilers I've tried evaluates them +** in the order encountered. +*/ +#ifdef __cplusplus +#ifndef __MEMWATCH_C +#ifdef MEMWATCH +#ifndef MEMWATCH_NOCPP +extern int mwNCur; +extern const char *mwNFile; +extern int mwNLine; +class MemWatch { +public: + MemWatch(); + ~MemWatch(); + }; +void * operator new(size_t); +void * operator new(size_t,const char *,int); +void * operator new[] (size_t,const char *,int); // hjc 07/16/02 +void operator delete(void *); +#define mwNew new(__FILE__,__LINE__) +#define mwDelete (mwNCur=1,mwNFile=__FILE__,mwNLine=__LINE__),delete +#endif /* MEMWATCH_NOCPP */ +#endif /* MEMWATCH */ +#endif /* !__MEMWATCH_C */ +#endif /* __cplusplus */ + +#endif /* 980317: disabled C++ */ + +#endif /* __MEMWATCH_H */ + +/* EOF MEMWATCH.H */ diff --git a/client/src/thirdparty/memwatch/memwatch.lsm b/client/src/thirdparty/memwatch/memwatch.lsm new file mode 100644 index 0000000..a7742a3 --- /dev/null +++ b/client/src/thirdparty/memwatch/memwatch.lsm @@ -0,0 +1,15 @@ +Begin3 +Title: memwatch +Version: 2.71 +Entered-date: 2002-09-18 +Description: fault tolerant ANSI-C source code memory leak and corruption detection +Keywords: memwatch debugging library memory leak source code ansi c +Author: johan@linkdata.se +Maintained-by: johan@linkdata.se +Primary-site: ftp.linkdata.se /pub/memwatch + 42K memwatch-2.71.tar.gz +Alternate-site: +Original-site: ftp.linkdata.se /pub/memwatch +Platforms: all +Copying-policy: GPL +End diff --git a/client/src/thirdparty/memwatch/test.c b/client/src/thirdparty/memwatch/test.c new file mode 100644 index 0000000..b4d847c --- /dev/null +++ b/client/src/thirdparty/memwatch/test.c @@ -0,0 +1,116 @@ + +/* +** NOTE: Running this program in a Win32 or Unix environment +** will probably result in a segmentation fault or protection +** error. These errors may be caused by MEMWATCH when it is +** looking at memory to see if it owns it, or may be caused by +** the test program writing to memory it does not own. +** +** MEMWATCH has two functions called 'mwIsReadAddr()' and +** 'mwIsSafeAddr()', which are system-specific. +** If they are implemented for your system, and works +** correctly, MEMWATCH will identify garbage pointers and +** avoid causing segmentation faults, GP's etc. +** +** If they are NOT implemented, count on getting the core +** dumped when running this test program! As of this writing, +** the safe-address checking has been implemented for Win32 +** and ANSI-C compliant systems. The ANSI-C checking traps +** SIGSEGV and uses setjmp/longjmp to resume processing. +** +** Note for Win95 users: The Win32 IsBadReadPtr() and its +** similar functions can return incorrect values. This has +** not happened under WinNT, though, just Win95. +** +** 991009 Johan Lindh +** +*/ + +#include +#include +#include "memwatch.h" + +#ifndef SIGSEGV +#error "SIGNAL.H does not define SIGSEGV; running this program WILL cause a core dump/crash!" +#endif + +#ifndef MEMWATCH +#error "You really, really don't want to run this without memwatch. Trust me." +#endif + +#if !defined(MW_STDIO) && !defined(MEMWATCH_STDIO) +#error "Define MW_STDIO and try again, please." +#endif + +int main() +{ + char *p; + + /* Collect stats on a line number basis */ + mwStatistics( 2 ); + + /* Slows things down, but OK for this test prg */ + /* mwAutoCheck( 1 ); */ + + TRACE("Hello world!\n"); + + p = malloc(210); + free(p); + p = malloc(20); + p = malloc(200); /* causes unfreed error */ + p[-1] = 0; /* causes underflow error */ + free(p); + + p = malloc(100); + p[ -(int)(sizeof(long)*8) ] = -1; /* try to damage MW's heap chain */ + free( p ); /* should cause relink */ + + mwSetAriFunc( mwAriHandler ); + ASSERT(1==2); + + mwLimit(1000000); + mwNoMansLand( MW_NML_ALL ); + + /* These may cause a general protection fault (segmentation fault) */ + /* They're here to help test the no-mans-land protection */ + if( mwIsSafeAddr(p+50000,1) ) { + TRACE("Killing byte at %p\n", p+50000); + *(p+50000) = 0; + } + if( mwIsSafeAddr(p+30000,1) ) { + TRACE("Killing byte at %p\n", p+30000); + *(p+30000) = 0; + } + if( mwIsSafeAddr(p+1000,1) ) { + TRACE("Killing byte at %p\n", p+1000); + *(p+1000) = 0; + } + if( mwIsSafeAddr(p-100,1) ) { + TRACE("Killing byte at %p\n", p-100); + *(p-100) = 0; + } + + /* This may cause a GP fault as well, since MW data buffers */ + /* have been damaged in the above killing spree */ + CHECK(); + + p = malloc(12000); + p[-5] = 1; + p[-10] = 2; + p[-15] = 3; + p[-20] = 4; + + /* This may cause a GP fault since MW's buffer list may have */ + /* been damaged by above killing, and it will try to repair it. */ + free(p); + + p = realloc(p,10); /* causes realloc: free'd from error */ + + /* May cause GP since MW will inspect the memory to see if it owns it. */ + free( (void*)main ); + + return 0; +} + +/* Comment out the following line to compile. */ +#error "Hey! Don't just compile this program, read the comments first!" diff --git a/client/src/thirdparty/stb_image.h b/client/src/thirdparty/stb_image.h index d60371b..b53e6e3 100644 --- a/client/src/thirdparty/stb_image.h +++ b/client/src/thirdparty/stb_image.h @@ -1,8 +1,8 @@ /* stb_image - v2.27 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk + no warranty implied; use at your own risk Do this: - #define STB_IMAGE_IMPLEMENTATION + #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: @@ -17,27 +17,27 @@ QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) + 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) + 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 + 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) + - 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. @@ -48,28 +48,28 @@ LICENSE RECENT REVISION HISTORY: - 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes - 2.26 (2020-07-13) many minor fixes - 2.25 (2020-02-02) fix warnings - 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically - 2.23 (2019-08-11) fix clang static analysis warning - 2.22 (2019-03-04) gif fixes, fix warnings - 2.21 (2019-02-25) fix typo in comment - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED See end of file for full revision history. @@ -77,46 +77,46 @@ RECENT REVISION HISTORY: ============================ Contributors ========================= Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine Simon Breuss (16-bit PNM) - John-Mark Allen - Carmelo J Fdez-Aguera + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine Simon Breuss (16-bit PNM) + John-Mark Allen + Carmelo J Fdez-Aguera Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski - Phil Jordan Dave Moore Roy Eltham - Hayaki Saito Nathan Reed Won Chun - Luke Graham Johan Duparc Nick Verigakis the Horde3D community - Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Eugene Golushkov Laurent Gomila Cort Stratton github:snagar - Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex - Cass Everitt Ryamond Barbiero github:grim210 - Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw - Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus - Josh Tobin Matthew Gregan github:poppolopoppo - Julian Raschke Gregory Mullen Christian Floisand github:darealshinji - Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 - Brad Weinberger Matvey Cherevko github:mosra - Luca Sas Alexander Veselov Zack Middleton [reserved] - Ryan C. Gordon [reserved] [reserved] - DO NOT ADD YOUR NAME HERE + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko github:mosra + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE - Jacko Dirks + Jacko Dirks To add your name to the credits, pick a random blank space in the middle and fill it. 80% of merge conflicts on stb PRs are due to people adding their name at the end @@ -619,19 +619,19 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #ifndef STBI_NO_THREAD_LOCALS #if defined(__cplusplus) && __cplusplus >= 201103L - #define STBI_THREAD_LOCAL thread_local + #define STBI_THREAD_LOCAL thread_local #elif defined(__GNUC__) && __GNUC__ < 5 - #define STBI_THREAD_LOCAL __thread + #define STBI_THREAD_LOCAL __thread #elif defined(_MSC_VER) - #define STBI_THREAD_LOCAL __declspec(thread) + #define STBI_THREAD_LOCAL __declspec(thread) #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) - #define STBI_THREAD_LOCAL _Thread_local + #define STBI_THREAD_LOCAL _Thread_local #endif #ifndef STBI_THREAD_LOCAL - #if defined(__GNUC__) - #define STBI_THREAD_LOCAL __thread - #endif + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif #endif #endif @@ -678,7 +678,7 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #ifndef STBI_MALLOC #define STBI_MALLOC(sz) malloc(sz) #define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) +#define STBI_FREE(p) if (p) free(p) #endif #ifndef STBI_REALLOC_SIZED @@ -737,9 +737,9 @@ static int stbi__cpuid3(void) { int res; __asm { - mov eax,1 - cpuid - mov res,edx + mov eax,1 + cpuid + mov res,edx } return res; } @@ -855,7 +855,7 @@ static void stbi__stdio_skip(void *user, int n) fseek((FILE*) user, n, SEEK_CUR); ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ if (ch != EOF) { - ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ } } @@ -981,7 +981,7 @@ static int stbi__err(const char *str) static void *stbi__malloc(size_t size) { - return STBI_MALLOC(size); + return STBI_MALLOC(size); } // stb_image uses ints pervasively, including for offset calculations. @@ -1028,7 +1028,7 @@ static int stbi__mad2sizes_valid(int a, int b, int add) static int stbi__mad3sizes_valid(int a, int b, int c, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); + stbi__addsizes_valid(a*b*c, add); } // returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow @@ -1036,7 +1036,7 @@ static int stbi__mad3sizes_valid(int a, int b, int c, int add) static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); } #endif @@ -1110,8 +1110,8 @@ STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_fli } #define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ - ? stbi__vertically_flip_on_load_local \ - : stbi__vertically_flip_on_load_global) + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) #endif // STBI_THREAD_LOCAL static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) @@ -1153,15 +1153,15 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); } #endif #ifndef STBI_NO_TGA // test tga last because it's a crappy test! if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); + return stbi__tga_load(s,x,y,comp,req_comp, ri); #endif return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); @@ -1177,7 +1177,7 @@ static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int chan if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling STBI_FREE(orig); return reduced; @@ -1193,7 +1193,7 @@ static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int chan if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff STBI_FREE(orig); return enlarged; @@ -1207,19 +1207,19 @@ static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) stbi_uc *bytes = (stbi_uc *)image; for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } } } @@ -1231,8 +1231,8 @@ static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int byt stbi_uc *bytes = (stbi_uc *)image; for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; } } #endif @@ -1243,21 +1243,21 @@ static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); if (result == NULL) - return NULL; + return NULL; // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); if (ri.bits_per_channel != 8) { - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; } // @TODO: move stbi__convert_format to here if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); } return (unsigned char *) result; @@ -1269,22 +1269,22 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); if (result == NULL) - return NULL; + return NULL; // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); if (ri.bits_per_channel != 16) { - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; } // @TODO: move stbi__convert_format16 to here // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); } return (stbi__uint16 *) result; @@ -1294,8 +1294,8 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); } } #endif @@ -1321,10 +1321,10 @@ static FILE *stbi__fopen(char const *filename, char const *mode) wchar_t wMode[64]; wchar_t wFilename[1024]; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) - return 0; + return 0; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) - return 0; + return 0; #if defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != _wfopen_s(&f, wFilename, wMode)) @@ -1335,7 +1335,7 @@ static FILE *stbi__fopen(char const *filename, char const *mode) #elif defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) - f=0; + f=0; #else f = fopen(filename, mode); #endif @@ -1360,8 +1360,8 @@ STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req stbi__start_file(&s,f); result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } @@ -1373,8 +1373,8 @@ STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, stbi__start_file(&s,f); result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } @@ -1429,7 +1429,7 @@ STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int * result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); } return result; @@ -1442,16 +1442,16 @@ static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int unsigned char *data; #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; } #endif data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__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"); } @@ -1513,8 +1513,8 @@ 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); + result = stbi_is_hdr_from_file(f); + fclose(f); } return result; } @@ -1579,25 +1579,25 @@ static void stbi__refill_buffer(stbi__context *s) int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; + // 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; + 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++; + return *s->img_buffer++; if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; + stbi__refill_buffer(s); + return *s->img_buffer++; } return 0; } @@ -1608,10 +1608,10 @@ stbi_inline static stbi_uc stbi__get8(stbi__context *s) 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; + 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; @@ -1625,16 +1625,16 @@ static void stbi__skip(stbi__context *s, int n) { if (n == 0) return; // already there! if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; + 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; - } + 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; } @@ -1646,25 +1646,25 @@ static void stbi__skip(stbi__context *s, int 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; + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; - memcpy(buffer, s->img_buffer, blen); + memcpy(buffer, s->img_buffer, blen); - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } } if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; } else - return 0; + return 0; } #endif @@ -1742,34 +1742,34 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); + 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; + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE } STBI_FREE(data); @@ -1799,34 +1799,34 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); } for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE } STBI_FREE(data); @@ -1845,14 +1845,14 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) // 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); - } + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } } if (n < comp) { - for (i=0; i < x*y; ++i) { - output[i*comp + n] = data[i*comp + n]/255.0f; - } + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } } STBI_FREE(data); return output; @@ -1871,18 +1871,18 @@ static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) // 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); - } + 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; @@ -1942,18 +1942,18 @@ typedef struct // definition of jpeg image component struct { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; + 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 + 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 @@ -1986,38 +1986,38 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) unsigned int code; // build size list for each symbol (from JPEG spec) for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); h->size[k] = 0; // compute actual symbols (from jpeg spec) code = 0; k = 0; for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; } h->maxcode[j] = 0xffffffff; // build non-spec acceleration table; 255 is flag for not-accelerated memset(h->fast, 255, 1 << FAST_BITS); for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } + 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; } @@ -2028,42 +2028,42 @@ 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]; + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } } } static void stbi__grow_buffer_unsafe(stbi__jpeg *j) { do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; } while (j->code_bits <= 24); } @@ -2083,12 +2083,12 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) 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]; + 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 @@ -2099,16 +2099,16 @@ stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) // 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 (temp < h->maxcode[k]) + break; if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; + // error! code not found + j->code_bits -= 16; + return -1; } if (k > j->code_bits) - return -1; + return -1; // convert the huffman code to the symbol id c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; @@ -2165,7 +2165,7 @@ stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) // where does it appear in the 8x8 matrix coded as row-major? static const stbi_uc stbi__jpeg_dezigzag[64+15] = { - 0, 1, 8, 16, 9, 2, 3, 10, + 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, @@ -2199,34 +2199,34 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman // 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]); - } - } + 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; } @@ -2240,19 +2240,19 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__ if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - diff = t ? stbi__extend_receive(j, t) : 0; + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * (1 << j->succ_low)); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * (1 << j->succ_low)); } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); } return 1; } @@ -2265,115 +2265,115 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__ 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; + int shift = j->succ_low; - if (j->eob_run) { - --j->eob_run; - return 1; - } + if (j->eob_run) { + --j->eob_run; + return 1; + } - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * (1 << shift)); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); - } - } - } while (k <= j->spec_end); + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); } else { - // refinement scan for these AC coefficients + // refinement scan for these AC coefficients - short bit = (short) (1 << j->succ_low); + short bit = (short) (1 << j->succ_low); - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } + // 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; } @@ -2383,8 +2383,8 @@ 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; + if (x < 0) return 0; + if (x > 255) return 255; } return (stbi_uc) x; } @@ -2438,54 +2438,54 @@ static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) // columns for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } } for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); + // 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); } } @@ -2505,78 +2505,78 @@ static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) // 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) + __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) + __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) + __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) + __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)); \ - } + { \ + __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) + 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) + 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); \ - } + { \ + /* 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)); @@ -2605,56 +2605,56 @@ static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) dct_pass(bias_0, 10); { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); } // row pass dct_pass(bias_1, 17); { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); } #undef dct_const @@ -2716,50 +2716,50 @@ static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) // 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)); \ + 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); \ + /* 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 @@ -2786,23 +2786,23 @@ static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) #define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } #define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); #undef dct_trn16 #undef dct_trn32 @@ -2816,51 +2816,51 @@ static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) dct_pass(vshrn_n_s32, 16); { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - // again, these can translate into one instruction, but often don't. + // again, these can translate into one instruction, but often don't. #define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } #define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } #define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); #undef dct_trn8_8 #undef dct_trn8_16 @@ -2889,7 +2889,7 @@ static stbi_uc stbi__get_marker(stbi__jpeg *j) x = stbi__get8(j->s); if (x != 0xff) return STBI__MARKER_none; while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes + x = stbi__get8(j->s); // consume repeated 0xff fill bytes return x; } @@ -2916,123 +2916,123 @@ 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; - } + 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; - } + 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; + } } } @@ -3040,25 +3040,25 @@ static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) { int i; for (i=0; i < 64; ++i) - data[i] *= dequant[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); - } - } - } + // 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); + } + } + } } } @@ -3066,99 +3066,99 @@ 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 STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; } // check for comment block or APP blocks if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } - stbi__skip(z->s, L); - return 1; + stbi__skip(z->s, L); + return 1; } return stbi__err("unknown marker","Corrupt JPEG"); @@ -3173,32 +3173,32 @@ static int stbi__process_scan_header(stbi__jpeg *z) 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 id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; } { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } } return 1; @@ -3208,20 +3208,20 @@ static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) { int i; for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } } return why; } @@ -3240,22 +3240,22 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); s->img_n = c; for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; } if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); z->rgb = 0; for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); } if (scan != STBI__SCAN_load) return 1; @@ -3263,15 +3263,15 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; } // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios // and I've never seen a non-corrupted JPEG file actually use them for (i=0; i < s->img_n; ++i) { - if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); - if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); } // compute interleaved mcu info @@ -3284,35 +3284,35 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } } return 1; @@ -3338,13 +3338,13 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) 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); - } + 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; @@ -3356,46 +3356,46 @@ 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->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; } j->restart_interval = 0; if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; m = stbi__get_marker(j); while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); } if (j->progressive) - stbi__jpeg_finish(j); + stbi__jpeg_finish(j); return 1; } // static jfif-centered resampling (across block boundaries) typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); + int w, int hs); #define stbi__div4(x) ((stbi_uc) ((x) >> 2)) @@ -3414,7 +3414,7 @@ static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc * int i; STBI_NOTUSED(hs); for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); return out; } @@ -3425,17 +3425,17 @@ static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc stbi_uc *input = in_near; if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; + // if 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]); + 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]; @@ -3453,17 +3453,17 @@ static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc // 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; + 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); + 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); @@ -3479,8 +3479,8 @@ static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stb int i=0,t0,t1; if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; } t1 = 3*in_near[0] + in_far[0]; @@ -3489,86 +3489,86 @@ static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stb // because we need to handle the filter boundary conditions. for (; i < ((w-1) & ~7); i += 8) { #if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); #elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); #endif - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; } t0 = t1; @@ -3576,10 +3576,10 @@ static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stb 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); + 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); @@ -3595,8 +3595,8 @@ static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_ 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]; + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; return out; } @@ -3607,24 +3607,24 @@ static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc { int i; for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; } } @@ -3638,127 +3638,127 @@ static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc cons // 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 + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } } #endif #ifdef STBI_NEON // in this version, step=3 support would be easy to add. but is there demand? if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } } #endif for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; } } #endif @@ -3772,9 +3772,9 @@ static void stbi__setup_jpeg(stbi__jpeg *j) #ifdef STBI_SSE2 if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; } #endif @@ -3825,9 +3825,9 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; + decode_n = 1; else - decode_n = z->s->img_n; + decode_n = z->s->img_n; // nothing to do if no components requested; check this now to avoid // accessing uninitialized coutput[0] later @@ -3835,139 +3835,139 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp // resample and color-convert { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; - stbi__resample res_comp[4]; + stbi__resample res_comp[4]; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; } } @@ -4000,8 +4000,8 @@ static int stbi__jpeg_test(stbi__context *s) 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; + stbi__rewind( j->s ); + return 0; } if (x) *x = j->s->img_x; if (y) *y = j->s->img_y; @@ -4073,40 +4073,40 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int memset(sizes, 0, sizeof(sizes)); memset(z->fast, 0, sizeof(z->fast)); for (i=0; i < num; ++i) - ++sizes[sizelist[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"); + 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]; + 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]; - } + 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; } @@ -4144,12 +4144,12 @@ stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) static void stbi__fill_bits(stbi__zbuf *z) { do { - if (z->code_buffer >= (1U << z->num_bits)) { - z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ - return; - } - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; } while (z->num_bits <= 24); } @@ -4170,8 +4170,8 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) // 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 (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]; @@ -4186,17 +4186,17 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { int b,s; if (a->num_bits < 16) { - if (stbi__zeof(a)) { - return -1; /* report error for unexpected end of data. */ - } - stbi__fill_bits(a); + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); } b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; } return stbi__zhuffman_decode_slowpath(a, z); } @@ -4211,8 +4211,8 @@ static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room limit = old_limit = (unsigned) (z->zout_end - z->zout_start); if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); while (cur + n > limit) { - if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); - limit *= 2; + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; } q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); STBI_NOTUSED(old_limit); @@ -4241,41 +4241,41 @@ 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); } - } - } + 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); } + } + } } } @@ -4294,34 +4294,34 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) 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; + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; } if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; n = 0; while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - } else if (c == 18) { - c = stbi__zreceive(a,7)+11; - } else { - return stbi__err("bad codelengths", "Corrupt PNG"); - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } } if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; @@ -4334,24 +4334,24 @@ static int stbi__parse_uncompressed_block(stbi__zbuf *a) stbi_uc header[4]; int len,nlen,k; if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard + 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; + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; } if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); // now fill header the normal way while (k < 4) - header[k++] = stbi__zget8(a); + 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; + if (!stbi__zexpand(a, a->zout, len)) return 0; memcpy(a->zout, a->zbuffer, len); a->zbuffer += len; a->zout += len; @@ -4405,26 +4405,26 @@ 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; + if (!stbi__parse_zlib_header(a)) return 0; a->num_bits = 0; a->code_buffer = 0; do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } } while (!final); return 1; } @@ -4447,11 +4447,11 @@ STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int 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; + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; } else { - STBI_FREE(a.zout_start); - return NULL; + STBI_FREE(a.zout_start); + return NULL; } } @@ -4468,11 +4468,11 @@ STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, i 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; + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; } else { - STBI_FREE(a.zout_start); - return NULL; + STBI_FREE(a.zout_start); + return NULL; } } @@ -4482,9 +4482,9 @@ STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer 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); + return (int) (a.zout - a.zout_start); else - return -1; + return -1; } STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) @@ -4495,11 +4495,11 @@ STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int 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; + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; } else { - STBI_FREE(a.zout_start); - return NULL; + STBI_FREE(a.zout_start); + return NULL; } } @@ -4509,9 +4509,9 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char 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); + return (int) (a.zout - a.zout_start); else - return -1; + return -1; } #endif @@ -4545,7 +4545,7 @@ static int stbi__check_png_header(stbi__context *s) static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; int i; for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); return 1; } @@ -4618,186 +4618,186 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; - int filter = *raw++; + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); - if (depth < 8) { - if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } } // we make a separate pass to expand bits to pixels; for performance, // this could run two scanlines behind the above code, so it won't // intefere with filtering but will still be in the cache. if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } } return 1; @@ -4810,38 +4810,38 @@ static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint3 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); + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); // de-interlacing final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); if (!final) return stbi__err("outofmem", "Out of memory"); for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } } a->out = final; @@ -4859,16 +4859,16 @@ static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) 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; - } + 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; - } + 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; } @@ -4884,16 +4884,16 @@ static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int ou STBI_ASSERT(out_n == 2 || out_n == 4); if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } + 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; } @@ -4910,22 +4910,22 @@ static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int 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; - } + 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; - } + 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; @@ -4968,11 +4968,11 @@ STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_conve } #define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ - ? stbi__unpremultiply_on_load_local \ - : stbi__unpremultiply_on_load_global) + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) #define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ - ? stbi__de_iphone_flag_local \ - : stbi__de_iphone_flag_global) + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) #endif // STBI_THREAD_LOCAL static void stbi__de_iphone(stbi__png *z) @@ -4982,39 +4982,39 @@ static void stbi__de_iphone(stbi__png *z) 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; - } + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } } } @@ -5038,161 +5038,161 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (scan == STBI__SCAN_type) return 1; for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); - s->img_y = stbi__get32be(s); - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - return 1; - } + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); } } @@ -5201,25 +5201,25 @@ static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, st void *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth <= 8) - ri->bits_per_channel = 8; - else if (p->depth == 16) - ri->bits_per_channel = 16; - else - return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; } STBI_FREE(p->out); p->out = NULL; STBI_FREE(p->expanded); p->expanded = NULL; @@ -5246,8 +5246,8 @@ static int stbi__png_test(stbi__context *s) 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; + stbi__rewind( p->s ); + return 0; } if (x) *x = p->s->img_x; if (y) *y = p->s->img_y; @@ -5269,8 +5269,8 @@ static int stbi__png_is16(stbi__context *s) if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) return 0; if (p.depth != 16) { - stbi__rewind(p.s); - return 0; + stbi__rewind(p.s); + return 0; } return 1; } @@ -5331,17 +5331,17 @@ static int stbi__bitcount(unsigned int a) static int stbi__shiftsigned(unsigned int v, int shift, int bits) { static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, }; static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, + 0, 0,0,1,0,2,4,6,0, }; if (shift < 0) - v <<= -shift; + v <<= -shift; else - v >>= shift; + v >>= shift; STBI_ASSERT(v < 256); v >>= (8-bits); STBI_ASSERT(bits >= 0 && bits <= 8); @@ -5359,24 +5359,24 @@ static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) { // BI_BITFIELDS specifies masks explicitly, don't override if (compress == 3) - return 1; + return 1; if (compress == 0) { - if (info->bpp == 16) { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } else if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - // otherwise, use defaults, which is all-0 - info->mr = info->mg = info->mb = info->ma = 0; - } - return 1; + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; } return 0; // error } @@ -5397,68 +5397,68 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) 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); + 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); + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); } if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); info->bpp = stbi__get16le(s); if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes - if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - stbi__bmp_set_mask_defaults(info, compress); - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->extra_read += 12; - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - // V4/V5 header - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs - stbi__bmp_set_mask_defaults(info, compress); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } } return (void *) 1; } @@ -5476,7 +5476,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req info.all_a = 255; if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set + return NULL; // error code already set flip_vertically = ((int) s->img_y) > 0; s->img_y = abs((int) s->img_y); @@ -5491,160 +5491,160 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req all_a = info.all_a; if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - info.extra_read - 24) / 3; + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; } else { - if (info.bpp < 16) - psize = (info.offset - info.extra_read - info.hsz) >> 2; + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; } if (psize == 0) { - if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { - return stbi__errpuc("bad offset", "Corrupt BMP"); - } + if (info.offset != s->callback_already_read + (s->img_buffer - s->img_buffer_original)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } } if (info.bpp == 24 && ma == 0xff000000) - s->img_n = 3; + s->img_n = 3; else - s->img_n = ma ? 4 : 3; + s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; + target = req_comp; else - target = s->img_n; // if they want monochrome, we'll post-convert + target = s->img_n; // if they want monochrome, we'll post-convert // sanity-check size if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); + return stbi__errpuc("too large", "Corrupt BMP"); out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - info.extra_read - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } } // if alpha channel is all 0s, replace with all 255s if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; + 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; - } - } + 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 + 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; @@ -5663,80 +5663,80 @@ static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) // only RGB or RGBA (incl. 16bit) or grey allowed if (is_rgb16) *is_rgb16 = 0; switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; } } static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) { - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything + 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) @@ -5748,14 +5748,14 @@ static int stbi__tga_test(stbi__context *s) 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 + 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 ( (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 @@ -5826,8 +5826,8 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // do a tiny bit of precessing if ( tga_image_type >= 8 ) { - tga_image_type -= 8; - tga_is_RLE = 1; + tga_image_type -= 8; + tga_is_RLE = 1; } tga_inverted = 1 - ((tga_inverted >> 5) & 1); @@ -5836,7 +5836,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req 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"); + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); // tga info *x = tga_width; @@ -5844,7 +5844,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req if (comp) *comp = tga_comp; if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); + return stbi__errpuc("too large", "Corrupt TGA"); tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); @@ -5853,143 +5853,143 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req 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); - } + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } } else { - // do I need to load a palette? - if ( tga_indexed) - { - if (tga_palette_len == 0) { /* you have to have at least one entry! */ - STBI_FREE(tga_data); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } } // swap RGB - if the source data was RGB16, it already is in the right order if (tga_comp >= 3 && !tga_rgb16) { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } + 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); + 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; + tga_x_origin = tga_y_origin = 0; STBI_NOTUSED(tga_palette_start); // OK, done return tga_data; @@ -6013,33 +6013,33 @@ static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) count = 0; while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } } return 1; @@ -6057,11 +6057,11 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // Check identifier if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); + 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"); + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); // Skip 6 reserved bytes. stbi__skip(s, 6 ); @@ -6069,7 +6069,7 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // 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"); + 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); @@ -6081,7 +6081,7 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // 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"); + 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: @@ -6094,7 +6094,7 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // 8: Duotone // 9: Lab color if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + 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) ); @@ -6111,19 +6111,19 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // 1: RLE compressed compression = stbi__get16be(s); if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); // Check size if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); + return stbi__errpuc("too large", "Corrupt PSD"); // Create the destination image. if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; } else - out = (stbi_uc *) stbi__malloc(4 * w*h); + out = (stbi_uc *) stbi__malloc(4 * w*h); if (!out) return stbi__errpuc("outofmem", "Out of memory"); pixelCount = w*h; @@ -6133,110 +6133,110 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // 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 + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop - // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } } // remove weird white matte from PSD if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } } // convert to desired output format if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure } if (comp) *comp = 4; @@ -6259,8 +6259,8 @@ 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; + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; return 1; } @@ -6270,13 +6270,13 @@ static int stbi__pic_test_core(stbi__context *s) int i; if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; + return 0; for(i=0;i<84;++i) - stbi__get8(s); + stbi__get8(s); if (!stbi__pic_is4(s,"PICT")) - return 0; + return 0; return 1; } @@ -6291,10 +6291,10 @@ 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); - } + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } } return dest; @@ -6305,8 +6305,8 @@ 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]; + 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) @@ -6315,105 +6315,105 @@ static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *c stbi__pic_packet packets[10]; // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. + // for the same channel in multiple packets. do { - stbi__pic_packet *packet; + stbi__pic_packet *packet; - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); - packet = &packets[num_packets++]; + packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); - act_comp |= packet->channel; + act_comp |= packet->channel; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); } while (chained); *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); + switch (packet->type) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); - case 0: {//uncompressed - int x; + case 0: {//uncompressed + int x; - for(x=0;xchannel,dest)) - return 0; - break; - } + for(x=0;xchannel,dest)) + return 0; + break; + } - case 1://Pure RLE - { - int left=width, i; + case 1://Pure RLE + { + int left=width, i; - while (left>0) { - stbi_uc count,value[4]; + while (left>0) { + stbi_uc count,value[4]; - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - if (count > left) - count = (stbi_uc) left; + if (count > left) + count = (stbi_uc) left; - if (!stbi__readval(s,packet->channel,value)) return 0; + if (!stbi__readval(s,packet->channel,value)) return 0; - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - if (count >= 128) { // Repeated - stbi_uc value[4]; + if (count >= 128) { // Repeated + stbi_uc value[4]; - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); - if (!stbi__readval(s,packet->channel,value)) - return 0; + if (!stbi__readval(s,packet->channel,value)) + return 0; - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } } return result; @@ -6428,7 +6428,7 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c if (!comp) comp = &internal_comp; for (i=0; i<92; ++i) - stbi__get8(s); + stbi__get8(s); x = stbi__get16be(s); y = stbi__get16be(s); @@ -6449,8 +6449,8 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; + STBI_FREE(result); + result=0; } *px = x; *py = y; @@ -6520,10 +6520,10 @@ static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], in { 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; + 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; } } @@ -6531,7 +6531,7 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in { 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"); + return stbi__err("not GIF", "Corrupt GIF"); version = stbi__get8(s); if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); @@ -6553,7 +6553,7 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in if (is_info) return 1; if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); return 1; } @@ -6563,9 +6563,9 @@ static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); if (!g) return stbi__err("outofmem", "Out of memory"); if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; + STBI_FREE(g); + stbi__rewind( s ); + return 0; } if (x) *x = g->w; if (y) *y = g->h; @@ -6581,7 +6581,7 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) // 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); + stbi__out_gif_code(g, g->codes[code].prefix); if (g->cur_y >= g->max_y) return; @@ -6591,22 +6591,22 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) c = &g->color_table[g->codes[code].suffix * 4]; if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; + 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; + g->cur_x = g->start_x; + g->cur_y += g->step; - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } } } @@ -6627,9 +6627,9 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) 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; + 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 @@ -6638,60 +6638,60 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) 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 (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - stbi__out_gif_code(g, (stbi__uint16) code); + stbi__out_gif_code(g, (stbi__uint16) code); - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } } } @@ -6708,166 +6708,166 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i // on first frame, any non-written pixels get the background colour (non-transparent) first_frame = 0; if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) - return stbi__errpuc("too large", "GIF image is too large"); - pcount = g->w * g->h; - g->out = (stbi_uc *) stbi__malloc(4 * pcount); - g->background = (stbi_uc *) stbi__malloc(4 * pcount); - g->history = (stbi_uc *) stbi__malloc(pcount); - if (!g->out || !g->background || !g->history) - return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); - // image is treated as "transparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to the color that was there the previous frame. - memset(g->out, 0x00, 4 * pcount); - memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) - memset(g->history, 0x00, pcount); // pixels that were affected previous frame - first_frame = 1; + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; } else { - // second frame - how do we dispose of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); } // clear my history; memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; - // if the width of the specified rectangle is 0, that means - // we may not see *any* pixels or the image is malformed; - // to make sure this is caught, move the current y down to - // max_y (which is what out_gif_code checks). - if (w == 0) - g->cur_y = g->max_y; + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; - g->lflags = stbi__get8(s); + g->lflags = stbi__get8(s); - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); - o = stbi__process_gif_raster(s, g); - if (!o) return NULL; + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } - return o; - } + return o; + } - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } } } @@ -6885,85 +6885,85 @@ static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **dela static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - int out_size = 0; - int delays_size = 0; + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; - STBI_NOTUSED(out_size); - STBI_NOTUSED(delays_size); + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; - if (out) { - void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); - if (!tmp) - return stbi__load_gif_main_outofmem(&g, out, delays); - else { - out = (stbi_uc*) tmp; - out_size = layers * stride; - } + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } - if (delays) { - int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); - if (!new_delays) - return stbi__load_gif_main_outofmem(&g, out, delays); - *delays = new_delays; - delays_size = layers * sizeof(int); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (!out) - return stbi__load_gif_main_outofmem(&g, out, delays); - out_size = layers * stride; - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - if (!*delays) - return stbi__load_gif_main_outofmem(&g, out, delays); - delays_size = layers * sizeof(int); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - *z = layers; - return out; + *z = layers; + return out; } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); + return stbi__errpuc("not GIF", "Image was not as a gif type."); } } @@ -6977,16 +6977,16 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req u = stbi__gif_load_next(s, &g, comp, req_comp, 0); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { - *x = g.w; - *y = g.h; + *x = g.w; + *y = g.h; - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); } else if (g.out) { - // if there was an error and we allocated an image buffer, free it! - STBI_FREE(g.out); + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); } // free buffers needed for multiple frame loading; @@ -7010,8 +7010,8 @@ static int stbi__hdr_test_core(stbi__context *s, const char *signature) { int i; for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; + if (stbi__get8(s) != signature[i]) + return 0; stbi__rewind(s); return 1; } @@ -7021,8 +7021,8 @@ static int stbi__hdr_test(stbi__context* s) int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); stbi__rewind(s); if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); } return r; } @@ -7036,14 +7036,14 @@ static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) 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++] = 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; @@ -7053,27 +7053,27 @@ static char *stbi__hdr_gettoken(stbi__context *z, char *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; + 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; - } + 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; + } } } @@ -7094,13 +7094,13 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re // Check identifier headerToken = stbi__hdr_gettoken(s,buffer); if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); + 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; + 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"); @@ -7126,83 +7126,83 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re if (req_comp == 0) req_comp = 3; if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); + return stbi__errpf("too large", "HDR image is too large"); // Read data hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); + return stbi__errpf("outofmem", "Out of memory"); // Load image data // image data is stored as some number of sca if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } + // 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; + // Read RLE-encoded data + scanline = NULL; - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); } return hdr_data; @@ -7220,31 +7220,31 @@ static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) if (!comp) comp = &dummy; if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 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; + 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; + stbi__rewind( s ); + return 0; } token = stbi__hdr_gettoken(s,buffer); if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; + 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; + stbi__rewind( s ); + return 0; } token += 3; *x = (int) strtol(token, NULL, 10); @@ -7262,16 +7262,16 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) info.all_a = 255; p = stbi__bmp_parse_header(s, &info); if (p == NULL) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } if (x) *x = s->img_x; if (y) *y = s->img_y; if (comp) { - if (info.bpp == 24 && info.ma == 0xff000000) - *comp = 3; - else - *comp = info.ma ? 4 : 3; + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; } return 1; } @@ -7285,29 +7285,29 @@ static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) if (!y) y = &dummy; if (!comp) comp = &dummy; if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } stbi__skip(s, 6); channelCount = stbi__get16be(s); if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } *y = stbi__get32be(s); *x = stbi__get32be(s); depth = stbi__get16be(s); if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } *comp = 4; return 1; @@ -7317,25 +7317,25 @@ static int stbi__psd_is16(stbi__context *s) { int channelCount, depth; if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } stbi__skip(s, 6); channelCount = stbi__get16be(s); if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } STBI_NOTUSED(stbi__get32be(s)); STBI_NOTUSED(stbi__get32be(s)); depth = stbi__get16be(s); if (depth != 16) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } return 1; } @@ -7352,8 +7352,8 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) if (!comp) comp = &dummy; if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; + stbi__rewind(s); + return 0; } stbi__skip(s, 88); @@ -7361,37 +7361,37 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) *x = stbi__get16be(s); *y = stbi__get16be(s); if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; + stbi__rewind( s); + return 0; } if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } stbi__skip(s, 8); do { - stbi__pic_packet *packet; + stbi__pic_packet *packet; - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } + 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); @@ -7419,8 +7419,8 @@ static int stbi__pnm_test(stbi__context *s) p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; + stbi__rewind( s ); + return 0; } return 1; } @@ -7432,7 +7432,7 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); if (ri->bits_per_channel == 0) - return 0; + return 0; if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); @@ -7442,15 +7442,15 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req if (comp) *comp = s->img_n; if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) - return stbi__errpuc("too large", "PNM too large"); + return stbi__errpuc("too large", "PNM too large"); out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8)); if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure + 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; } @@ -7463,14 +7463,14 @@ static int stbi__pnm_isspace(char c) 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); + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); - if (stbi__at_eof(s) || *c != '#') - break; + if (stbi__at_eof(s) || *c != '#') + break; - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); } } @@ -7484,8 +7484,8 @@ 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); + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); } return value; @@ -7506,8 +7506,8 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; + stbi__rewind(s); + return 0; } *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm @@ -7523,11 +7523,11 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) maxv = stbi__pnm_getinteger(s, &c); // read max value if (maxv > 65535) - return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); else if (maxv > 255) - return 16; + return 16; else - return 8; + return 8; } static int stbi__pnm_is16(stbi__context *s) @@ -7575,7 +7575,7 @@ static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) // test tga last because it's a crappy test! #ifndef STBI_NO_TGA if (stbi__tga_info(s, x, y, comp)) - return 1; + return 1; #endif return stbi__err("unknown image type", "Image not of any known type, or corrupt"); } @@ -7599,12 +7599,12 @@ static int stbi__is_16_main(stbi__context *s) #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; + 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) @@ -7620,12 +7620,12 @@ STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) STBIDEF int stbi_is_16_bit(char const *filename) { - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; } STBIDEF int stbi_is_16_bit_from_file(FILE *f) @@ -7672,185 +7672,185 @@ STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user /* revision history: - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version */ diff --git a/client/src/thirdparty/stb_leakcheck.h b/client/src/thirdparty/stb_leakcheck.h deleted file mode 100644 index 19ee6e7..0000000 --- a/client/src/thirdparty/stb_leakcheck.h +++ /dev/null @@ -1,194 +0,0 @@ -// stb_leakcheck.h - v0.6 - quick & dirty malloc leak-checking - public domain -// LICENSE -// -// See end of file. - -#ifdef STB_LEAKCHECK_IMPLEMENTATION -#undef STB_LEAKCHECK_IMPLEMENTATION // don't implement more than once - -// if we've already included leakcheck before, undefine the macros -#ifdef malloc -#undef malloc -#undef free -#undef realloc -#endif - -#ifndef STB_LEAKCHECK_OUTPUT_PIPE -#define STB_LEAKCHECK_OUTPUT_PIPE stdout -#endif - -#include -#include -#include -#include -#include -typedef struct malloc_info stb_leakcheck_malloc_info; - -struct malloc_info -{ - const char *file; - int line; - size_t size; - stb_leakcheck_malloc_info *next,*prev; -}; - -static stb_leakcheck_malloc_info *mi_head; - -void *stb_leakcheck_malloc(size_t sz, const char *file, int line) -{ - stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) malloc(sz + sizeof(*mi)); - if (mi == NULL) return mi; - mi->file = file; - mi->line = line; - mi->next = mi_head; - if (mi_head) - mi->next->prev = mi; - mi->prev = NULL; - mi->size = (int) sz; - mi_head = mi; - return mi+1; -} - -void stb_leakcheck_free(void *ptr) -{ - if (ptr != NULL) { - stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) ptr - 1; - mi->size = ~mi->size; - #ifndef STB_LEAKCHECK_SHOWALL - if (mi->prev == NULL) { - assert(mi_head == mi); - mi_head = mi->next; - } else - mi->prev->next = mi->next; - if (mi->next) - mi->next->prev = mi->prev; - free(mi); - #endif - } -} - -void *stb_leakcheck_realloc(void *ptr, size_t sz, const char *file, int line) -{ - if (ptr == NULL) { - return stb_leakcheck_malloc(sz, file, line); - } else if (sz == 0) { - stb_leakcheck_free(ptr); - return NULL; - } else { - stb_leakcheck_malloc_info *mi = (stb_leakcheck_malloc_info *) ptr - 1; - if (sz <= mi->size) - return ptr; - else { - #ifdef STB_LEAKCHECK_REALLOC_PRESERVE_MALLOC_FILELINE - void *q = stb_leakcheck_malloc(sz, mi->file, mi->line); - #else - void *q = stb_leakcheck_malloc(sz, file, line); - #endif - if (q) { - memcpy(q, ptr, mi->size); - stb_leakcheck_free(ptr); - } - return q; - } - } -} - -static void stblkck_internal_print(const char *reason, stb_leakcheck_malloc_info *mi) -{ -#if defined(_MSC_VER) && _MSC_VER < 1900 // 1900=VS 2015 - // Compilers that use the old MS C runtime library don't have %zd - // and the older ones don't even have %lld either... however, the old compilers - // without "long long" don't support 64-bit targets either, so here's the - // compromise: - #if _MSC_VER < 1400 // before VS 2005 - fprintf(STB_LEAKCHECK_OUTPUT_PIPE, "%s: %s (%4d): %8d bytes at %p\n", reason, mi->file, mi->line, (int)mi->size, (void*)(mi+1)); - #else - fprintf(STB_LEAKCHECK_OUTPUT_PIPE, "%s: %s (%4d): %16lld bytes at %p\n", reason, mi->file, mi->line, (long long)mi->size, (void*)(mi+1)); - #endif -#else - // Assume we have %zd on other targets. - #ifdef __MINGW32__ - __mingw_fprintf(STB_LEAKCHECK_OUTPUT_PIPE, "%s: %s (%4d): %zd bytes at %p\n", reason, mi->file, mi->line, mi->size, (void*)(mi+1)); - #else - fprintf(STB_LEAKCHECK_OUTPUT_PIPE, "%s: %s (%4d): %zd bytes at %p\n", reason, mi->file, mi->line, mi->size, (void*)(mi+1)); - #endif -#endif -} - -void stb_leakcheck_dumpmem(void) -{ - stb_leakcheck_malloc_info *mi = mi_head; - while (mi) { - if ((ptrdiff_t) mi->size >= 0) - stblkck_internal_print("LEAKED", mi); - mi = mi->next; - } - #ifdef STB_LEAKCHECK_SHOWALL - mi = mi_head; - while (mi) { - if ((ptrdiff_t) mi->size < 0) - stblkck_internal_print("FREED ", mi); - mi = mi->next; - } - #endif -} -#endif // STB_LEAKCHECK_IMPLEMENTATION - -#if !defined(INCLUDE_STB_LEAKCHECK_H) || !defined(malloc) -#define INCLUDE_STB_LEAKCHECK_H - -#include // we want to define the macros *after* stdlib to avoid a slew of errors - -#define malloc(sz) stb_leakcheck_malloc(sz, __FILE__, __LINE__) -#define free(p) stb_leakcheck_free(p) -#define realloc(p,sz) stb_leakcheck_realloc(p,sz, __FILE__, __LINE__) - -extern void * stb_leakcheck_malloc(size_t sz, const char *file, int line); -extern void * stb_leakcheck_realloc(void *ptr, size_t sz, const char *file, int line); -extern void stb_leakcheck_free(void *ptr); -extern void stb_leakcheck_dumpmem(void); - -#endif // INCLUDE_STB_LEAKCHECK_H - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/