From 3e8300ecbb587abf7402fd7f9d7c397a4cef530b Mon Sep 17 00:00:00 2001 From: mataha Date: Mon, 14 Aug 2023 19:21:57 +0200 Subject: [PATCH] Update stb This commit and, by extension, PR attempts to update `stb` in the most straightforward way possible. Whether that was successful is left to the reader's judgement... Aside from synchronizing the library with the main repo, the following fixes from unmerged PRs were included in this change: - stb#1299: stb_rect_pack: make rect_height_compare a stable sort - stb#1402: stb_image: Fix "unused invalid_chunk" with STBI_FAILURE_USERMSG - stb#1404: stb_image: Fix gif two_back memory address - stb#1420: stb_image: Improve error reporting if file operations fail within *_from_file functions - stb#1445: stb_vorbis: Few static analyzers fixes - stb#1487: stb_vorbis: Fix residue classdata bounding for f->temp_memory_required - stb#1490: stb_vorbis: Fix broken clamp in codebook_decode_deinterleave_repeat - stb#1496: stb_image: Fix pnm only build - stb#1497: stb_image: Fix memory leaks if stbi__convert failed - stb#1498: stb_vorbis: Fix memory leaks in stb_vorbis - stb#1499: stb_vorbis: Minor change to prevent the undefined behavior - left shift of a negative value - stb#1500: stb_vorbis: Fix signed integer overflow Additional, relevant minor fixes I felt didn't warrant a separate PR: - make `dct()` and `dctjpeg()` accept a stride parameter - don't expose `stbi__flip_vertically_on_write` outside std_image_write - remove `ifndef` shenanigans from `stbi__load_main()`; reorder formats - remove `bpc` parameter from `stbi__load_main()` due to no PSD support - check for running out of bits from stream in `stbi__jpeg_get_bit*()`s - check for 0xFF while skipping trash in stbi__skip_jpeg_junk_at_end()` - return APP0 marker type through `stbi__jpeg->jfif` member - consistently use `tinyprint` and `fileno.h` in programs utilizing stb - fix `-?` not echoing help when using getopt in programs utilizing stb - fix `-m` not being registered in `getopt()` in `memzoom.c` - reorder some comments mangled by `clang-format` Signed-off-by: mataha --- dsp/core/core.h | 5 +- dsp/core/dct.c | 79 ++-- third_party/stb/README.cosmo | 29 +- third_party/stb/README.txt | 143 +++--- third_party/stb/stb_image.c | 619 ++++++++++++++++++++------ third_party/stb/stb_image.h | 15 +- third_party/stb/stb_image_resize.c | 30 +- third_party/stb/stb_image_write.c | 361 ++++++++------- third_party/stb/stb_image_write.h | 1 - third_party/stb/stb_image_write_png.c | 4 +- third_party/stb/stb_rect_pack.c | 8 +- third_party/stb/stb_vorbis.c | 367 +++++++++------ third_party/stb/stb_vorbis.h | 15 + tool/viz/derasterize.c | 11 +- tool/viz/memzoom.c | 23 +- tool/viz/od16.c | 19 +- tool/viz/printansi.c | 15 +- tool/viz/printimage.c | 17 +- tool/viz/printvideo.c | 19 +- 19 files changed, 1137 insertions(+), 643 deletions(-) diff --git a/dsp/core/core.h b/dsp/core/core.h index 714f3a392f65..eadf040f937a 100644 --- a/dsp/core/core.h +++ b/dsp/core/core.h @@ -9,8 +9,9 @@ int mulaw(int); int unmulaw(int); void *double2byte(long, const void *, double, double) vallocesque; void *byte2double(long, const void *, double, double) vallocesque; -void *dct(float[8][8], float, float, float, float, float); -void *dctjpeg(float[8][8]); +void *dct(float[restrict hasatleast 8][8], unsigned, + float, float, float, float, float); +void *dctjpeg(float[restrict hasatleast 8][8], unsigned); double det3(const double[3][3]) nosideeffect; void *inv3(double[restrict 3][3], const double[restrict 3][3], double); void *matmul3(double[restrict 3][3], const double[3][3], const double[3][3]); diff --git a/dsp/core/dct.c b/dsp/core/dct.c index d69660a196cf..5d4bdd7e5cca 100644 --- a/dsp/core/dct.c +++ b/dsp/core/dct.c @@ -18,40 +18,40 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "dsp/core/core.h" -#define DCT(A, B, C, D, E, F, G, H, T, C0, C1, C2, C3, C4) \ - do { \ - T z1, z2, z3, z4, z5, z11, z13; \ - T t0, t1, t2, t3, t4, t5, t6, t7, t10, t11, t12, t13; \ - t0 = A + H; \ - t7 = A - H; \ - t1 = B + G; \ - t6 = B - G; \ - t2 = C + F; \ - t5 = C - F; \ - t3 = D + E; \ - t4 = D - E; \ - t10 = t0 + t3; \ - t13 = t0 - t3; \ - t11 = t1 + t2; \ - t12 = t1 - t2; \ - A = t10 + t11; \ - E = t10 - t11; \ - z1 = (t12 + t13) * C0; \ - C = t13 + z1; \ - G = t13 - z1; \ - t10 = t4 + t5; \ - t11 = t5 + t6; \ - t12 = t6 + t7; \ - z5 = (t10 - t12) * C1; \ - z2 = t10 * C2 + z5; \ - z4 = t12 * C3 + z5; \ - z3 = t11 * C4; \ - z11 = t7 + z3; \ - z13 = t7 - z3; \ - F = z13 + z2; \ - D = z13 - z2; \ - B = z11 + z4; \ - H = z11 - z4; \ +#define DCT(A, B, C, D, E, F, G, H, T, C0, C1, C2, C3, C4) \ + do { \ + T z1, z2, z3, z4, z5, z11, z13; \ + T t0, t1, t2, t3, t4, t5, t6, t7, t10, t11, t12, t13; \ + t0 = A + H; \ + t7 = A - H; \ + t1 = B + G; \ + t6 = B - G; \ + t2 = C + F; \ + t5 = C - F; \ + t3 = D + E; \ + t4 = D - E; \ + t10 = t0 + t3; \ + t13 = t0 - t3; \ + t11 = t1 + t2; \ + t12 = t1 - t2; \ + A = t10 + t11; \ + E = t10 - t11; \ + z1 = (t12 + t13) * C0; \ + C = t13 + z1; \ + G = t13 - z1; \ + t10 = t4 + t5; \ + t11 = t5 + t6; \ + t12 = t6 + t7; \ + z5 = (t10 - t12) * C1; \ + z2 = t10 * C2 + z5; \ + z4 = t12 * C3 + z5; \ + z3 = t11 * C4; \ + z11 = t7 + z3; \ + z13 = t7 - z3; \ + F = z13 + z2; \ + D = z13 - z2; \ + B = z11 + z4; \ + H = z11 - z4; \ } while (0) /** @@ -65,20 +65,21 @@ * * @cost ~100ns */ -void *dct(float M[8][8], float c0, float c1, float c2, float c3, float c4) { +void *dct(float M[restrict hasatleast 8][8], unsigned stride, + float c0, float c1, float c2, float c3, float c4) { unsigned y, x; - for (y = 0; y < 8; ++y) { + for (y = 0; y < stride * 8; y += stride) { DCT(M[y][0], M[y][1], M[y][2], M[y][3], M[y][4], M[y][5], M[y][6], M[y][7], float, c0, c1, c2, c3, c4); } - for (x = 0; x < 8; ++x) { + for (x = 0; x < stride * 8; x += stride) { DCT(M[0][x], M[1][x], M[2][x], M[3][x], M[4][x], M[5][x], M[6][x], M[7][x], float, c0, c1, c2, c3, c4); } return M; } -void *dctjpeg(float M[8][8]) { - return dct(M, .707106781f, .382683433f, .541196100f, 1.306562965f, +void *dctjpeg(float M[restrict hasatleast 8][8], unsigned stride) { + return dct(M, stride, .707106781f, .382683433f, .541196100f, 1.306562965f, .707106781f); } diff --git a/third_party/stb/README.cosmo b/third_party/stb/README.cosmo index ca3b785c9cb3..b2c7717ad61c 100644 --- a/third_party/stb/README.cosmo +++ b/third_party/stb/README.cosmo @@ -5,8 +5,8 @@ LOCAL CHANGES - Removed undefined behavior - Removed BMP [endian code made it 100x slower than PNG/JPEG] - Removed PIC [never heard of it] - - Removed TGA [consider imaagemagick convert command] - - Removed PSD [consider imaagemagick convert command] + - Removed TGA [consider imagemagick convert command] + - Removed PSD [consider imagemagick convert command] - Removed HDR [mine eyes and wikipedia agree stb gamma math is off] - Patched PNG loading edge case - Fixed code C standard says is undefined @@ -14,10 +14,25 @@ LOCAL CHANGES - Removed unnecessary ifdefs - Removed MSVC torture code -SYNCHRONIZATION POINT +SYNCHRONIZATION POINT (`--date=format:"%a %b %d %H:%M:%S %Y %z"`) - commit f67165c2bb2af3060ecae7d20d6f731173485ad0 - Author: Sean Barrett - Date: Mon Oct 28 09:30:02 2019 -0700 + commit 5736b15f7ea0ffb08dd38af21067c314d6a3aae9 + Author: Sean Barrett + Date: Sun Jan 29 10:46:04 2023 -0800 - Update README.md + re-add perlin noise again + +ADDITIONAL CHANGES/FIXES: + + - https://github.com/nothings/stb/pull/1299 + - https://github.com/nothings/stb/pull/1402 + - https://github.com/nothings/stb/pull/1404 + - https://github.com/nothings/stb/pull/1420 + - https://github.com/nothings/stb/pull/1445 + - https://github.com/nothings/stb/pull/1487 + - https://github.com/nothings/stb/pull/1490 + - https://github.com/nothings/stb/pull/1496 + - https://github.com/nothings/stb/pull/1497 + - https://github.com/nothings/stb/pull/1498 + - https://github.com/nothings/stb/pull/1499 + - https://github.com/nothings/stb/pull/1500 diff --git a/third_party/stb/README.txt b/third_party/stb/README.txt index 4c691527140a..70a4c065a44e 100644 --- a/third_party/stb/README.txt +++ b/third_party/stb/README.txt @@ -1,13 +1,12 @@ -/* - * stb_image - v2.23 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.28 - public domain image loader - http://nothings.org/stb * no warranty implied; use at your own risk * * [heavily modified by justine tunney] * * JPEG baseline & progressive (12 bpc/arithmetic not supported, same - * as stock IJG lib) PNG 1/2/4/8/16-bit-per-channel + * as stock IJG lib) + * PNG 1/2/4/8/16-bit-per-channel * GIF (*comp always reports as 4-channel) - * HDR (radiance rgbE format) * PNM (PPM and PGM binary only) * * Animated GIF still needs a proper API, but here's one way to do it: @@ -18,45 +17,53 @@ * * ============================ 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 (ImageNet JPGs) - * Mikhail Morozov (1-bit BMP) - * Optimizations & bugfixes Anael Seghezzi (is-16-bit query) - * Fabian "ryg" Giesen - * Arseny Kapoulkine - * John-Mark Allen + * 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) + * Optimizations & bugfixes Jeremy Sawicki (ImageNet JPGs) + * Fabian "ryg" Giesen Mikhail Morozov (1-bit BMP) + * Arseny Kapoulkine Anael Seghezzi (is-16-bit query) + * John-Mark Allen Simon Breuss (16-bit PNM) * Carmelo J Fdez-Aguera * * Bug & warning fixes - * Marc LeBlanc David Woo Guillaume George Martins Mozeiko - * Christpher Lloyd Jerry Jansson Joseph Thomson 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 - * Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar - * Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex - * Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 - * Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw - * Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus - * Julian Raschke Gregory Mullen Baldur Karlsson - * github:poppolopoppo Christian Floisand Kevin Schmidt JR Smith - * github:darealshinji Blazej Dariusz Roszkowski github:Michaelangel007 - */ - -/* - * DOCUMENTATION + * Marc LeBlanc Laurent Gomila JR Smith + * Christpher Lloyd Sergio Gonzalez Matvey Cherevko + * Phil Jordan Ryamond Barbiero Zack Middleton + * Hayaki Saito Engin Manap + * Luke Graham Dale Weiler Martins Mozeiko + * Thomas Ruf Neil Bickford Blazej Dariusz Roszkowski + * Janez Zemva Gregory Mullen Roy Eltham + * Jonathan Blow Kevin Schmidt + * Eugene Golushkov Brad Weinberger the Horde3D community + * Aruelien Pocheville Alexander Veselov github:rlyeh + * Cass Everitt [reserved] github:romigrou + * Paul Du Bois github:svdijk + * Philipp Wiesemann Guillaume George github:snagar + * Josh Tobin Joseph Thomson github:Zelex + * Julian Raschke Dave Moore github:grim210 + * Baldur Karlsson Won Chun github:sammyhw + * Nick Verigakis github:phprus + * Luca Sas github:poppolopoppo + * Ryan C. Gordon Michal Cichon github:darealshinji + * David Woo Tero Hanninen github:Michaelangel007 + * Jerry Jansson Cort Stratton github:mosra + * Thibault Reuille [reserved] + * Nathan Reed [reserved] + * Johan Duparc Aldo Culquicondor + * Ronny Chevalier Oriol Ferrer Jacko Dirks + * John Bartholomew Matthew Gregan + * Ken Hamada Christian Floisand + * + * ============================ Documentation ========================= * * Limitations: * - no 12-bit-per-channel JPEG @@ -70,14 +77,15 @@ * // ... x = width, y = height, n = # 8-bit components per pixel ... * // ... replace '0' with '1'..'4' to force that many components per pixel * // ... but 'n' will always be the number that it would have been if you - * said 0 stbi_image_free(data) + * // ... said 0 + * stbi_image_free(data); * * Standard parameters: * int *x -- outputs image width in pixels * int *y -- outputs image height in pixels * int *channels_in_file -- outputs # of image components in image file * int desired_channels -- if non-zero, # of image components requested in - * result + * result * * The return value from an image loader is an 'unsigned char *' which points * to the pixel data, or NULL on an allocation failure or if the image is @@ -110,6 +118,32 @@ * * Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. * + * To query the width, height and component count of an image without having to + * decode the full file, you can use the stbi_info family of functions: + * + * int x,y,n,ok; + * ok = stbi_info(filename, &x, &y, &n); + * // returns ok=1 and sets x, y, n if image is a supported format, + * // 0 otherwise. + * + * Note that stb_image pervasively uses ints in its public API for sizes, + * including sizes of memory buffers. This is now part of the API and thus + * hard to change without causing breakage. As a result, the various image + * loaders all have certain limits on image size; these differ somewhat + * by format but generally boil down to either just under 2GB or just under + * 1GB. When the decoded image would be larger than this, stb_image decoding + * will fail. + * + * Additionally, stb_image will reject image files that have any of their + * dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, + * which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, + * the only way to have an image with such dimensions load correctly + * is for it to have a rather extreme aspect ratio. Either way, the + * assumption here is that such larger images are likely to be malformed + * or malicious. If you do need to load an image with individual dimensions + * larger than that, and it still fits in the overall size limit, you can + * #define STBI_MAX_DIMENSIONS on your own to be something larger. + * * =========================================================================== * * I/O callbacks @@ -163,11 +197,10 @@ * * iPhone PNG support: * - * By default we convert iphone-formatted PNGs back to RGB, even though - * they are internally encoded differently. You can disable this conversion - * by calling stbi_convert_iphone_png_to_rgb(0), in which case - * you will always just get the native iphone "format" through (which - * is BGR stored in RGB). + * We optionally support converting iPhone-formatted PNGs (which store + * premultiplied BGRA) back to RGB, even though they're internally encoded + * differently. To enable this conversion, call + * stbi_convert_iphone_png_to_rgb(1). * * Call stbi_set_unpremultiply_on_load(1) as well to force a divide per * pixel to remove any premultiplied alpha *only* if the image file explicitly @@ -191,9 +224,18 @@ * - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still * want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB * + * - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater + * than that size (in either width or height) without further processing. + * This is to let programs in the wild set an upper bound to prevent + * denial-of-service attacks on untrusted data, as one could generate a + * valid image of gigantic dimensions and force stb_image to allocate a + * huge block of memory and spend disproportionate time decoding it. By + * default this is set to (1 << 24), which is 16777216, but that's still + * very big. + * */ -/* stb_image_resize - v0.96 - public domain image resizing +/* stb_image_resize - v0.97 - public domain image resizing * by Jorge L Rodriguez (@VinoBS) - 2014 * http://github.com/nothings/stb * @@ -214,9 +256,7 @@ * output_pixels, out_w, out_h, 0, * num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) * // WRAP/REFLECT/ZERO - */ - -/* + * * DOCUMENTATION * * SRGB & FLOATING POINT REPRESENTATION @@ -348,6 +388,7 @@ * Nathan Reed: warning fixes * * REVISIONS + * 0.97 (2020-02-02) fixed warning * 0.96 (2019-03-04) fixed warnings * 0.95 (2017-07-23) fixed warnings * 0.94 (2017-03-18) fixed warnings diff --git a/third_party/stb/stb_image.c b/third_party/stb/stb_image.c index 0f330ea99b9f..b05babb6e003 100644 --- a/third_party/stb/stb_image.c +++ b/third_party/stb/stb_image.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -50,14 +50,15 @@ Credit: Sean Barrett, et al.\\n\ #define idct_block_kernel stbi__idct_block #endif -#define ROL(w, k) ((w) << (k) | (w) >> (sizeof(w) * CHAR_BIT - (k))) +#define ROL(w, k) (((w) << (k)) | ((w) >> (-(k) & (sizeof(w) * CHAR_BIT - 1)))) #ifndef STBI_REALLOC_SIZED #define STBI_REALLOC_SIZED(p, oldsz, newsz) realloc(p, newsz) #endif -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif // stbi__context structure is our basic context used by all images, so it // contains all the IO context, plus some basic image information @@ -69,6 +70,7 @@ typedef struct { int read_from_callbacks; int buflen; unsigned char buffer_start[128]; + int callback_already_read; unsigned char *img_buffer, *img_buffer_end; unsigned char *img_buffer_original, *img_buffer_original_end; } stbi__context; @@ -82,6 +84,7 @@ static void stbi__start_mem(stbi__context *s, unsigned char const *buffer, int len) { s->io.read = NULL; s->read_from_callbacks = 0; + s->callback_already_read = 0; s->img_buffer = s->img_buffer_original = (unsigned char *)buffer; s->img_buffer_end = s->img_buffer_original_end = (unsigned char *)buffer + len; @@ -94,7 +97,8 @@ static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, s->io_user_data = user; s->buflen = sizeof(s->buffer_start); s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); s->img_buffer_original_end = s->img_buffer_end; } @@ -104,11 +108,16 @@ static int stbi__stdio_read(void *user, char *data, int size) { } static void stbi__stdio_skip(void *user, int n) { + int ch; fseek(user, n, SEEK_CUR); + ch = fgetc(user); + if (ch != EOF) { + ungetc(ch, user); + } } static int stbi__stdio_eof(void *user) { - return feof(user); + return feof(user) || ferror(user); } static stbi_io_callbacks stbi__stdio_callbacks = { @@ -167,8 +176,8 @@ const char *stbi_failure_reason(void) { static int stbi__err(const char *specific_details, const char *general_details) { - /* DebugBreak(); */ - /* WARNF("%s: %s", general_details, specific_details); */ + // DebugBreak(); + // WARNF("%s: %s", general_details, specific_details); stbi__g_failure_reason = general_details; return 0; } @@ -203,17 +212,27 @@ static int stbi__mul2sizes_valid(int a, int b) { return a <= INT_MAX / b; } -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +// returns 1 if "a * b + add" has no negative terms/factors +// and doesn't overflow static int stbi__mad2sizes_valid(int a, int b, int add) { return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a * b, add); } -// returns 1 if "a*b*c + add" has no negaive terms/factors and doesn't overflow +// returns 1 if "a * b * c + add" has no negative terms/factors +// and doesn't overflow static int stbi__mad3sizes_valid(int a, int b, int c, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a * b, c) && stbi__addsizes_valid(a * b * c, add); } +// returns 1 if "a * b * c * d + add" has no negative terms/factors +// and doesn't overflow +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); +} + // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) { if (!stbi__mad2sizes_valid(a, b, add)) return NULL; @@ -225,6 +244,44 @@ static void *stbi__malloc_mad3(int a, int b, int c, int add) { return xmalloc(a * b * c + add); } +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return xmalloc(a * b * c * d + add); +} + +// returns 1 if the sum of two signed ints is valid +// (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) { + if ((a >= 0) != (b >= 0)) { + // a and b have different signs, so no overflow + return 1; + } + if (a < 0 && b < 0) { + // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a >= INT_MIN - b; + } + return a <= INT_MAX - b; +} + +// returns 1 if the product of two signed shorts is valid, +// 0 on overflow. +static int stbi__mul2shorts_valid(short a, short b) { + if (b == 0 || b == -1) { + // multiplication by 0 is always 0; + // check for -1 so SHRT_MIN / b doesn't overflow + return 1; + } + if ((a >= 0) == (b >= 0)) { + // product is positive, so similar to mul2sizes_valid + return a <= SHRT_MAX / b; + } + if (b < 0) { + // same as a * b >= SHRT_MIN + return a <= SHRT_MIN / b; + } + return a >= SHRT_MIN / b; +} + #define stbi__errpf(x, y) \ ({ \ stbi__err(x, y); \ @@ -246,22 +303,22 @@ void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { } static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, - int req_comp, stbi__result_info *ri, int bpc) { + int req_comp, stbi__result_info *ri) { bzero(ri, sizeof(*ri)); ri->bits_per_channel = 8; ri->num_channels = 0; -#ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s, x, y, comp, req_comp, ri); -#endif -#ifndef STBI_NO_PNG + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) if (stbi__png_test(s)) return stbi__png_load(s, x, y, comp, req_comp, ri); -#endif -#ifndef STBI_NO_GIF if (stbi__gif_test(s)) return stbi__gif_load(s, x, y, comp, req_comp, ri); -#endif -#ifndef STBI_NO_PNM + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s, x, y, comp, req_comp, ri); if (stbi__pnm_test(s)) return stbi__pnm_load(s, x, y, comp, req_comp, ri); -#endif + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } @@ -334,12 +391,18 @@ static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int req_comp) { void *result; stbi__result_info ri; - result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + result = stbi__load_main(s, x, y, comp, req_comp, &ri); if (result == NULL) return NULL; + assert(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); if (ri.bits_per_channel != 8) { - assert(ri.bits_per_channel == 16); - result = - stbi__convert_16_to_8(result, *x, *y, req_comp == 0 ? *comp : req_comp); + // https://github.com/nothings/stb/pull/1497 + unsigned char *converted = stbi__convert_16_to_8( + (uint16_t *)result, *x, *y, req_comp == 0 ? *comp : req_comp); + if (converted == NULL) { + free(result); + return NULL; + } + result = converted; ri.bits_per_channel = 8; } // @TODO: move stbi__convert_format to here @@ -353,13 +416,20 @@ static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, static uint16_t *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { + void *result; stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + result = stbi__load_main(s, x, y, comp, req_comp, &ri); if (result == NULL) return NULL; + assert(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); if (ri.bits_per_channel != 16) { - assert(ri.bits_per_channel == 8); - result = stbi__convert_8_to_16((unsigned char *)result, *x, *y, - req_comp == 0 ? *comp : req_comp); + // https://github.com/nothings/stb/pull/1497 + uint16_t *converted = stbi__convert_8_to_16( + (unsigned char *)result, *x, *y, req_comp == 0 ? *comp : req_comp); + if (converted == NULL) { + free(result); + return NULL; + } + result = converted; ri.bits_per_channel = 16; } // @TODO: move stbi__convert_format16 to here @@ -395,7 +465,14 @@ unsigned char *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, 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); + // https://github.com/nothings/stb/pull/1420 + if (fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR)) { + // fseek() failed; we can no longer maintain the file cursor position + // guarantee of this function, so return null. + free(result); + return stbi__errpuc("bad file", + "fseek() failed; seek position unreliable"); + } } return result; } @@ -408,7 +485,14 @@ uint16_t *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, 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); + // https://github.com/nothings/stb/pull/1420 + if (fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR)) { + // fseek() failed; we can no longer maintain the file cursor position + // guarantee of this function, so return null. + free(result); + return (uint16_t *)stbi__errpuc( + "bad file", "fseek() failed; seek position unreliable"); + } } return result; } @@ -475,6 +559,7 @@ enum { STBI__SCAN_load = 0, STBI__SCAN_type, STBI__SCAN_header }; static void stbi__refill_buffer(stbi__context *s) { int n = (s->io.read)(s->io_user_data, (char *)s->buffer_start, s->buflen); + s->callback_already_read += (int)(s->img_buffer - s->img_buffer_original); if (n == 0) { // at end of file, treat same as if from memory, but need to handle case // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file @@ -509,6 +594,7 @@ forceinline int stbi__at_eof(stbi__context *s) { } 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; @@ -568,7 +654,7 @@ static uint32_t stbi__get32be(stbi__context *s) { // generic converter from built-in img_n to req_comp // individual types do this automatically as much as possible (e.g. jpeg // does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically +// and it never has alpha, so very few cases). png can automatically // interleave an alpha=255 channel, but falls back to this for other cases // // assume data buffer is malloced, so malloc a new one and free that one @@ -657,6 +743,9 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, break; default: assert(0); + free(data); + free(good); + return stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } @@ -750,6 +839,10 @@ static uint16_t *stbi__convert_format16(uint16_t *data, int img_n, int req_comp, break; default: assert(0); + free(data); + free(good); + return (uint16_t *)stbi__errpuc("unsupported", + "Unsupported format conversion"); } #undef STBI__CASE } @@ -848,8 +941,14 @@ static int stbi__build_huffman(stbi__huffman *h, int *count) { int i, j, k = 0; unsigned int code; // build size list for each symbol (from JPEG spec) - for (i = 0; i < 16; ++i) - for (j = 0; j < count[i]; ++j) h->size[k++] = (unsigned char)(i + 1); + for (i = 0; i < 16; ++i) { + for (j = 0; j < count[i]; ++j) { + h->size[k++] = (unsigned char)(i + 1); + if (k >= 257) { + return stbi__err("bad size list", "Corrupt JPEG"); + } + } + } h->size[k] = 0; // compute actual symbols (from jpeg spec) @@ -961,8 +1060,10 @@ forceinline int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) { for (k = FAST_BITS + 1;; ++k) if (temp < h->maxcode[k]) break; if (k == 17) { + WARNF("j->code_bits: %d", j->code_bits); // error! code not found j->code_bits -= 16; + WARNF("Symbol: %d", k); return -1; } @@ -970,6 +1071,10 @@ forceinline int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) { // convert the huffman code to the symbol id c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if (c < 0 || c >= 256) { + // symbol id out of bounds! + return -1; + } assert((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); @@ -979,7 +1084,7 @@ forceinline int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) { return h->values[c]; } -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - sgn = (int32_t)j->code_buffer >> 31; // sign bit is always in MSB + if (j->code_bits < n) { + // ran out of bits from stream, return 0s intead of continuing + return 0; + } + // sign bit is always in MSB; + // 0 if MSB clear (positive), 1 if MSB set (negative) + sgn = j->code_buffer >> 31; k = ROL(j->code_buffer, n); - assert(n >= 0 && n < (int)(sizeof(stbi__bmask) / sizeof(*stbi__bmask))); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); + return k + (stbi__jbias[n] & (sgn - 1)); } // get some unsigned bits forceinline int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { unsigned int k; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) { + // ran out of bits from stream, return 0s intead of continuing + return 0; + } k = ROL(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; @@ -1014,6 +1127,10 @@ forceinline int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { forceinline int stbi__jpeg_get_bit(stbi__jpeg *j) { unsigned int k; if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) { + // ran out of bits from stream, return 0s intead of continuing + return 0; + } k = j->code_buffer; j->code_buffer <<= 1; --j->code_bits; @@ -1038,12 +1155,18 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], int diff, dc, k, t, c, r, s, rs; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code", "Corrupt JPEG"); + if (t < 0 || t > 15) return stbi__err("bad huffman code", "Corrupt JPEG"); // 0 all the ac values now so we can do it 32-bits at a time bzero(data, 64 * sizeof(data[0])); diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) { + return stbi__err("bad delta", "Corrupt JPEG"); + } dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) { + return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + } data[0] = (short)(dc * dequant[0]); // decode AC components, see JPEG spec k = 1; @@ -1054,6 +1177,10 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) { + return stbi__err("bad huffman code", + "Combined length longer than code bits available"); + } j->code_buffer <<= s; j->code_bits -= s; // decode into unzigzag'd location @@ -1081,7 +1208,6 @@ static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) { int t; - short s; int diff, dc; if (j->spec_end != 0) { return stbi__err("can't merge dc and ac", "Corrupt JPEG"); @@ -1091,15 +1217,22 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], // first scan for DC coefficient, must be first bzero(data, 64 * sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) { + return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + } diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) { + return stbi__err("bad delta", "Corrupt JPEG"); + } dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; - s = dc; - s *= 1u << j->succ_low; - data[0] = s; /* (short)(dc << j->succ_low); */ + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) { + return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + } + data[0] = (short)(dc * (1u << j->succ_low)); } else { // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) data[0] += (short)(1 << j->succ_low); + if (stbi__jpeg_get_bit(j)) data[0] += (short)(1u << j->succ_low); } return 1; } @@ -1128,10 +1261,14 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length + if (s > j->code_bits) { + return stbi__err("bad huffman code", + "Combined length longer than code bits available"); + } j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (r / 256) * (1u << shift); + data[zig] = (short)((r >> 8) * (1u << shift)); } else { rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code", "Corrupt JPEG"); @@ -1148,15 +1285,13 @@ static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], } else { k += r; zig = stbi__jpeg_dezigzag[k++]; - data[zig] = stbi__extend_receive(j, s) * (1u << shift); + data[zig] = (short)(stbi__extend_receive(j, s) * (1u << shift)); } } } while (k <= j->spec_end); } else { // refinement scan for these AC coefficients - - bit = (short)(1 << j->succ_low); - + bit = (short)(1u << j->succ_low); if (j->eob_run) { --j->eob_run; for (k = j->spec_start; k <= j->spec_end; ++k) { @@ -1272,9 +1407,10 @@ forceinline unsigned char stbi__clamp(int x) { t1 += p2 + p4; \ t0 += p1 + p3; -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) { +static void stbi__idct_block(unsigned char *out, int out_stride, + short data[64]) { int i, val[64], *v = val; - stbi_uc *o; + unsigned char *o; short *d = data; // columns @@ -1337,7 +1473,8 @@ static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) { // sse2 integer IDCT. not the fastest possible implementation but it // produces bit-identical results to the generic C version so it's // fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { +static void stbi__idct_simd(unsigned char *out, int out_stride, + short data[64]) { // This is constructed to match our regular (generic) integer IDCT exactly. __m128i row0, row1, row2, row3, row4, row5, row6, row7; __m128i tmp; @@ -1539,7 +1676,8 @@ static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { // NEON integer IDCT. should produce bit-identical // results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { +static void stbi__idct_simd(unsigned char *out, int out_stride, + short data[64]) { int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); @@ -2024,6 +2162,10 @@ static int stbi__process_marker(stbi__jpeg *z, int m) { sizes[i] = stbi__get8(z->s); n += sizes[i]; } + if (n > 256) { + // Loop over i < n would write past end of values! + return stbi__err("bad DHT header", "Corrupt JPEG"); + } L -= 17; if (tc == 0) { if (!stbi__build_huffman(z->huff_dc + th, sizes)) return 0; @@ -2057,7 +2199,7 @@ static int stbi__process_marker(stbi__jpeg *z, int m) { for (i = 0; i < 5; ++i) if (stbi__get8(z->s) != tag[i]) ok = 0; L -= 5; - if (ok) z->jfif = 1; + if (ok) z->jfif = m; } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment static const unsigned char tag[6] = {'A', 'd', 'o', 'b', 'e', '\0'}; int ok = 1; @@ -2149,24 +2291,36 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) { stbi__context *s = z->s; int Lf, p, i, q, h_max = 1, v_max = 1, c; Lf = stbi__get16be(s); - if (Lf < 11) return stbi__err("bad SOF len", "Corrupt JPEG"); // JPEG + if (Lf < 11) { + // JPEG + return stbi__err("bad SOF len", "Corrupt JPEG"); + } p = stbi__get8(s); - if (p != 8) - return stbi__err("only 8-bit", - "JPEG format not supported: 8-bit only"); // JPEG baseline + if (p != 8) { + // JPEG baseline + return stbi__err("only 8-bit", "JPEG format not supported: 8-bit only"); + } s->img_y = stbi__get16be(s); - if (s->img_y == 0) - return stbi__err( - "no header height", - "JPEG format not supported: delayed height"); // Legal, but we don't - // handle it--but neither - // does IJG + if (s->img_y == 0) { + // Legal, but we don't handle it--but neither does IJG + return stbi__err("no header height", + "JPEG format not supported: delayed height"); + } s->img_x = stbi__get16be(s); - if (s->img_x == 0) - return stbi__err("0 width", "Corrupt JPEG"); // JPEG requires + if (s->img_x == 0) { + // JPEG requires + return stbi__err("0 width", "Corrupt JPEG"); + } + if (s->img_y > STBI_MAX_DIMENSIONS) { + return stbi__err("too large", "Very large image (corrupt?)"); + } + if (s->img_x > STBI_MAX_DIMENSIONS) { + return stbi__err("too large", "Very large image (corrupt?)"); + } c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) + 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; @@ -2201,6 +2355,18 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) { if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; } + // check that plane subsampling factors are integer ratios; + // our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i = 0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) { + return stbi__err("bad H", "Corrupt JPEG"); + } + if (v_max % z->img_comp[i].v != 0) { + return stbi__err("bad V", "Corrupt JPEG"); + } + } + // compute interleaved mcu info z->img_h_max = h_max; z->img_v_max = v_max; @@ -2271,6 +2437,27 @@ static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) { return 1; } +static int stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) { + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + while (x == 0xff) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + // decode image to YCbCr format static int stbi__decode_jpeg_image(stbi__jpeg *j) { int m; @@ -2286,26 +2473,22 @@ static int stbi__decode_jpeg_image(stbi__jpeg *j) { 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; - } - } + j->marker = stbi__skip_jpeg_junk_at_end(j); // if we reach eof without hitting a marker, stbi__get_marker() below // will fail and we'll eventually return 0 } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) m = stbi__get_marker(j); } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); uint32_t NL = stbi__get16be(j->s); if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); } else { - if (!stbi__process_marker(j, m)) return 0; + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); } - m = stbi__get_marker(j); } if (j->progressive) stbi__jpeg_finish(j); return 1; @@ -2470,9 +2653,10 @@ static unsigned char *stbi__resample_row_nearest(unsigned char *out, // this is a reduced-precision calculation of YCbCr-to-RGB introduced // to make sure the code produces the same results in both SIMD and scalar #define stbi__float2fixed(x) (((int)((x)*4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, - const stbi_uc *pcb, const stbi_uc *pcr, - int count, int step) { +static void stbi__YCbCr_to_RGB_row(unsigned char *out, const unsigned char *y, + const unsigned char *pcb, + const unsigned char *pcr, int count, + int step) { int i; for (i = 0; i < count; ++i) { int y_fixed = (y[i] << 20) + (1 << 19); // rounding @@ -2504,18 +2688,19 @@ static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; + out[0] = (unsigned char)r; + out[1] = (unsigned char)g; + out[2] = (unsigned char)b; out[3] = 255; out += step; } } #if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, - stbi_uc const *pcb, stbi_uc const *pcr, - int count, int step) { +static void stbi__YCbCr_to_RGB_simd(unsigned char *out, unsigned char const *y, + unsigned char const *pcb, + unsigned char const *pcr, int count, + int step) { int i = 0; #ifdef STBI_SSE2 @@ -2655,9 +2840,9 @@ static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; + out[0] = (unsigned char)r; + out[1] = (unsigned char)g; + out[2] = (unsigned char)b; out[3] = 255; out += step; } @@ -2721,6 +2906,13 @@ static unsigned char *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, decode_n = z->s->img_n; } + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { + stbi__cleanup_jpeg(z); + return NULL; + } + // resample and color-convert { int k; @@ -2875,7 +3067,10 @@ static dontinline void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { unsigned char *result; - stbi__jpeg *j = (stbi__jpeg *)malloc(sizeof(stbi__jpeg)); + stbi__jpeg *j; + j = malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + bzero(j, sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); result = load_jpeg_image(j, x, y, comp, req_comp); @@ -2887,6 +3082,8 @@ static int stbi__jpeg_test(stbi__context *s) { int r; stbi__jpeg *j; j = malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + bzero(j, sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); @@ -2909,6 +3106,8 @@ static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) { static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { int result; stbi__jpeg *j = (stbi__jpeg *)(malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + bzero(j, sizeof(stbi__jpeg)); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); free(j); @@ -2925,6 +3124,7 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { // fast-way is faster to check than jpeg huffman, but slow way is slower #define STBI__ZFAST_BITS 9 // accelerate all cases in default tables #define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet // zlib-style huffman encoding // (jpegs packs from left, zlib from right, so can't share code) @@ -2933,8 +3133,8 @@ typedef struct { uint16_t firstcode[16]; int maxcode[17]; uint16_t firstsymbol[16]; - unsigned char size[288]; - uint16_t value[288]; + unsigned char size[STBI__ZNSYMS]; + uint16_t value[STBI__ZNSYMS]; } stbi__zhuffman; forceinline int stbi__bit_reverse(int v, int bits) { @@ -3005,14 +3205,20 @@ typedef struct { stbi__zhuffman z_length, z_distance; } stbi__zbuf; +forceinline int stbi__zeof(stbi__zbuf *z) { + return (z->zbuffer >= z->zbuffer_end); +} + forceinline unsigned char stbi__zget8(stbi__zbuf *z) { - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; + return stbi__zeof(z) ? 0 : *z->zbuffer++; } static void stbi__fill_bits(stbi__zbuf *z) { do { - assert(z->code_buffer < (1u << z->num_bits)); + 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); @@ -3034,10 +3240,17 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) { k = stbi__bit_reverse(a->code_buffer, 16); for (s = STBI__ZFAST_BITS + 1;; ++s) if (k < z->maxcode[s]) break; - if (s == 16) return -1; // invalid code! + if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s]; - assert(z->size[b] == s); + if (b >= STBI__ZNSYMS) { + // some data was corrupt somewhere! + return -1; + } + if (z->size[b] != s) { + // was originally an assert, but report failure instead. + return -1; + } a->code_buffer >>= s; a->num_bits -= s; return z->value[b]; @@ -3045,7 +3258,12 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) { forceinline int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { int b, s; - if (a->num_bits < 16) stbi__fill_bits(a); + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; // report error for unexpected end of data. + } + stbi__fill_bits(a); + } b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { s = b >> 9; @@ -3058,12 +3276,18 @@ forceinline int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) { char *q; - int cur, limit, old_limit; + unsigned int cur, limit, old_limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit", "Corrupt PNG"); - cur = (int)(z->zout - z->zout_start); - limit = old_limit = (int)(z->zout_end - z->zout_start); - while (cur + n > limit) limit *= 2; + cur = (unsigned int)(z->zout - z->zout_start); + limit = old_limit = (unsigned)(z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned)n) { + return stbi__err("outofmem", "Out of memory"); + } + while (cur + n > limit) { + if (limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } q = (char *)STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); if (q == NULL) return stbi__err("outofmem", "Out of memory"); z->zout_start = q; @@ -3111,12 +3335,21 @@ static int stbi__parse_huffman_block(stbi__zbuf *a) { a->zout = zout; return 1; } + if (z >= 286) { + // per DEFLATE, length codes 286 and 287 + // must not appear in compressed data + return stbi__err("bad huffman code", "Corrupt PNG"); + } 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"); + if (z < 0 || z >= 30) { + // per DEFLATE, distance codes 30 and 31 + // must not appear in compressed data + 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) @@ -3171,11 +3404,12 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) { c = stbi__zreceive(a, 2) + 3; if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); fill = lencodes[n - 1]; - } else if (c == 17) + } else if (c == 17) { c = stbi__zreceive(a, 3) + 3; - else { - assert(c == 18); + } 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); @@ -3200,7 +3434,7 @@ static int stbi__parse_uncompressed_block(stbi__zbuf *a) { a->code_buffer >>= 8; a->num_bits -= 8; } - assert(a->num_bits == 0); + if (a->num_bits < 0) return stbi__err("zlib corrupt", "Corrupt PNG"); // now fill header the normal way while (k < 4) header[k++] = stbi__zget8(a); len = header[1] * 256 + header[0]; @@ -3221,6 +3455,8 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) { int cm = cmf & 15; /* int cinfo = cmf >> 4; */ int flg = stbi__zget8(a); + if (stbi__zeof(a)) + return stbi__err("bad zlib header", "Corrupt PNG"); // zlib spec if ((cmf * 256 + flg) % 31 != 0) return stbi__err("bad zlib header", "Corrupt PNG"); // zlib spec if (flg & 32) @@ -3233,7 +3469,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) { return 1; } -static const unsigned char stbi__zdefault_length[288] = { +static const unsigned char stbi__zdefault_length[STBI__ZNSYMS] = { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, @@ -3279,7 +3515,8 @@ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) { } else { if (type == 1) { // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length, stbi__zdefault_length, 288)) + 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; @@ -3493,10 +3730,9 @@ static int stbi__create_png_image_raw(stbi__png *a, unsigned char *raw, if (filter > 4) return stbi__err("invalid filter", "Corrupt PNG"); if (depth < 8) { - assert(img_width_bytes <= x); - cur += - x * out_n - img_width_bytes; // store output to the rightmost img_len - // bytes, so we can decode in place + if (img_width_bytes > x) return stbi__err("invalid width", "Corrupt PNG"); + // store output to the rightmost img_len bytes, so we can decode in place + cur += x * out_n - img_width_bytes; filter_bytes = 1; width = img_width_bytes; } @@ -3754,6 +3990,7 @@ static int stbi__create_png_image(stbi__png *a, unsigned char *image_data, a->s->img_x, a->s->img_y, depth, color); // de-interlacing final = 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}; @@ -3941,10 +4178,10 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { first = 0; if (c.length != 13) return stbi__err("bad IHDR len", "Corrupt PNG"); s->img_x = stbi__get32be(s); - if (s->img_x > (1 << 24)) - return stbi__err("too large", "Very large image (corrupt?)"); s->img_y = stbi__get32be(s); - if (s->img_y > (1 << 24)) + 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 && @@ -3972,15 +4209,14 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { 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 } + // even with SCAN_header, have to scan to see if we have a tRNS break; } @@ -4018,6 +4254,12 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { if (c.length != (uint32_t)s->img_n * 2) return stbi__err("bad tRNS len", "Corrupt PNG"); has_trans = 1; + // non-paletted with tRNS = constant alpha. + // if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { + ++s->img_n; + return 1; + } if (z->depth == 16) { for (k = 0; k < s->img_n; ++k) tc16[k] = (uint16_t)stbi__get16be(s); // copy the values as-is @@ -4035,9 +4277,13 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { 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; + // header scan definitely stops at first IDAT + if (pal_img_n) s->img_n = pal_img_n; return 1; } + if (c.length > (1u << 30)) + return stbi__err("IDAT size limit", + "IDAT section larger than 2^30 bytes"); if ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { uint32_t idata_limit_old = idata_limit; @@ -4101,7 +4347,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { } free(z->expanded); z->expanded = NULL; - stbi__get32be(s); /* nothings/stb#835 */ + // end of PNG chunk, read and skip CRC + stbi__get32be(s); return 1; } @@ -4109,7 +4356,7 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { // if critical, fail if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if ((c.type & (1 << 29)) == 0) { -#ifndef STBI_NO_FAILURE_STRINGS +#if !defined(STBI_NO_FAILURE_STRINGS) && !defined(STBI_FAILURE_USERMSG) // not threadsafe static char invalid_chunk[] = "XXXX PNG chunk not known"; invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); @@ -4134,10 +4381,13 @@ static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, 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) + if (p->depth <= 8) ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; else - ri->bits_per_channel = p->depth; + 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) { @@ -4284,6 +4534,10 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, g->bgindex = stbi__get8(s); g->ratio = stbi__get8(s); g->transparent = -1; + if (g->w > STBI_MAX_DIMENSIONS) + return stbi__err("too large", "Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) + return stbi__err("too large", "Very large image (corrupt?)"); if (comp != 0) { *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the // comments @@ -4297,6 +4551,7 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { stbi__gif *g = (stbi__gif *)malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); if (!stbi__gif_header(s, g, comp, 1)) { free(g); stbi__rewind(s); @@ -4445,7 +4700,7 @@ static unsigned char *stbi__gif_load_next(stbi__context *s, stbi__gif *g, 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 + // image is treated as "transparent" at the start - i.e. 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. @@ -4456,7 +4711,7 @@ static unsigned char *stbi__gif_load_next(stbi__context *s, stbi__gif *g, pcount); // pixels that were affected previous frame first_frame = 1; } else { - // second frame - how do we dispoase of the previous one? + // second frame - how do we dispose of the previous one? dispose = (g->eflags & 0x1C) >> 2; pcount = g->w * g->h; @@ -4479,10 +4734,10 @@ static unsigned char *stbi__gif_load_next(stbi__context *s, stbi__gif *g, } } } else { - // This is a non-disposal case eithe way, so just + // This is a non-disposal case either way, so just // leave the pixels as is, and they will become the new background // 1: do not dispose - // 0: not specified. + // 0: not specified. } // background is what out is after the undoing of the previou frame; @@ -4609,6 +4864,16 @@ static unsigned char *stbi__gif_load_next(stbi__context *s, stbi__gif *g, } } +static void *stbi__load_gif_main_outofmem(stbi__gif *g, unsigned char *out, + int **delays) { + free(g->out); + free(g->history); + free(g->background); + if (out) free(out); + if (delays && *delays) free(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { @@ -4618,6 +4883,8 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, unsigned char *two_back = 0; stbi__gif *g; int stride; + int out_size = 0; + int delays_size = 0; g = calloc(1, sizeof(stbi__gif)); if (delays) { *delays = 0; @@ -4631,21 +4898,35 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, ++layers; stride = g->w * g->h * 4; if (out) { - out = (unsigned char *)realloc(out, layers * stride); - if (!out) abort(); + void *tmp = (unsigned char *)STBI_REALLOC_SIZED(out, out_size, + layers * stride); + if (!tmp) + return stbi__load_gif_main_outofmem(g, out, delays); + else { + out = (unsigned char *)tmp; + out_size = layers * stride; + } if (delays) { - *delays = (int *)realloc(*delays, sizeof(int) * layers); - if (!*delays) abort(); + 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 = malloc(layers * stride); + if (!out) return stbi__load_gif_main_outofmem(g, out, delays); + out_size = layers * stride; if (delays) { *delays = 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; + two_back = out + ((layers - 2) * stride); } if (delays) { (*delays)[layers - 1U] = g->delay; @@ -4707,7 +4988,6 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) { // Known limitations: // Does not support comments in the header section // Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel static int stbi__pnm_test(stbi__context *s) { char p, t; @@ -4724,20 +5004,37 @@ static dontinline void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { unsigned char *out; - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, - (int *)&s->img_n)) { - return 0; + ri->bits_per_channel = + stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) return 0; + if (s->img_y > STBI_MAX_DIMENSIONS) { + return stbi__errpuc("too large", "Very large image (corrupt?)"); + } + if (s->img_x > STBI_MAX_DIMENSIONS) { + return stbi__errpuc("too large", "Very large image (corrupt?)"); } *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) { + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, + ri->bits_per_channel / 8, 0)) { return stbi__errpuc("too large", "PNM too large"); } - out = stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + out = stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, + ri->bits_per_channel / 8, 0); + if (!stbi__getn( + s, out, + s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + free(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } 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 (ri->bits_per_channel == 16) { + out = (unsigned char *)stbi__convert_format16( + (uint16_t *)out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } if (out == NULL) return out; // stbi__convert_format frees input on failure } return out; @@ -4766,6 +5063,12 @@ static int stbi__pnm_getinteger(stbi__context *s, char *c) { while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { value = value * 10 + (*c - '0'); *c = (char)stbi__get8(s); + // TODO INT_MAX + if ((value > 214748364) || (value == 214748364 && *c > '7')) { + return stbi__err( + "integer parse overflow", + "Parsing an integer in the PPM header overflowed a 32-bit int"); + } } return value; } @@ -4789,15 +5092,30 @@ static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) { c = (char)stbi__get8(s); stbi__pnm_skip_whitespace(s, &c); *x = stbi__pnm_getinteger(s, &c); // read width + if (*x == 0) { + return stbi__err("invalid_width", + "PPM image header had zero or overflowing width"); + } stbi__pnm_skip_whitespace(s, &c); *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) { + return stbi__err("invalid height", + "PPM image header had zero or overflowing height"); + } stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else { - return 1; - } + if (maxv > 65535) + return stbi__err("max value > 65535", + "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) { + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) return 1; + return 0; } static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) { @@ -4818,7 +5136,12 @@ static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) { } static int stbi__is_16_main(stbi__context *s) { +#ifndef STBI_NO_PNG if (stbi__png_is16(s)) return 1; +#endif +#ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; +#endif return 0; } @@ -4835,9 +5158,10 @@ int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) { int r; stbi__context s; long pos = ftell(f); + if (pos < 0) return stbi__err("bad file", "ftell() failed"); stbi__start_file(&s, f); r = stbi__info_main(&s, x, y, comp); - fseek(f, pos, SEEK_SET); + if (fseek(f, pos, SEEK_SET)) return stbi__err("bad file", "fseek() failed"); return r; } @@ -4854,9 +5178,10 @@ int stbi_is_16_bit_from_file(FILE *f) { int r; stbi__context s; long pos = ftell(f); + if (pos < 0) return stbi__err("bad file", "ftell() failed"); stbi__start_file(&s, f); r = stbi__is_16_main(&s); - fseek(f, pos, SEEK_SET); + if (fseek(f, pos, SEEK_SET)) return stbi__err("bad file", "fseek() failed"); return r; } diff --git a/third_party/stb/stb_image.h b/third_party/stb/stb_image.h index 74cc96ad7457..52e850e1ea94 100644 --- a/third_party/stb/stb_image.h +++ b/third_party/stb/stb_image.h @@ -14,12 +14,14 @@ enum { struct FILE; typedef struct { - int (*read)(void *user, char *data, - int size); // fill 'data' with 'size' bytes. return number of - // bytes actually read - void (*skip)(void *user, int n); // skip the next 'n' bytes, or 'unget' the - // last -n bytes if negative - int (*eof)(void *user); // returns nonzero if we are at end of file/data + // fill 'data' with 'size' bytes. return number of bytes actually read + int (*read)(void *user, char *data, int size); + + // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + void (*skip)(void *user, int n); + + // returns nonzero if we are at end of file/data + int (*eof)(void *user); } stbi_io_callbacks; // @@ -64,7 +66,6 @@ unsigned short *stbi_load_from_file_16(struct FILE *f, int *x, int *y, int desired_channels); // get a VERY brief reason for failure -// NOT THREADSAFE const char *stbi_failure_reason(void); // free the loaded image -- this is just free() diff --git a/third_party/stb/stb_image_resize.c b/third_party/stb/stb_image_resize.c index 22d6a3e7651c..d85f401833be 100644 --- a/third_party/stb/stb_image_resize.c +++ b/third_party/stb/stb_image_resize.c @@ -1,7 +1,7 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ │ │ │ Permission to use, copy, modify, and/or distribute this software for │ │ any purpose with or without fee is hereby granted, provided that the │ @@ -655,9 +655,14 @@ static void stbir__calculate_coefficients_upsample( total_filter += coefficient_group[i]; } - STBIR_ASSERT(stbir__filter_info_table[filter].kernel( - (float)(in_last_pixel + 1) + 0.5f - in_center_of_out, - 1 / scale) == 0); + // NOTE(fg): Not actually true in general, nor is there any reason to expect + // it should be. It would be true in exact math but is at best approximately + // true in floating-point math, and it would not make sense to try and put + // actual bounds on this here because it depends on the image aspect ratio + // which can get pretty extreme. + // STBIR_ASSERT(stbir__filter_info_table[filter].kernel( + // (float)(in_last_pixel + 1) + 0.5f - in_center_of_out, + // 1 / scale) == 0); STBIR_ASSERT(total_filter > 0.9); STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. @@ -701,9 +706,14 @@ static void stbir__calculate_coefficients_downsample( stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; } - STBIR_ASSERT(stbir__filter_info_table[filter].kernel( - (float)(out_last_pixel + 1) + 0.5f - out_center_of_in, - scale_ratio) == 0); + // NOTE(fg): Not actually true in general, nor is there any reason to expect + // it should be. It would be true in exact math but is at best approximately + // true in floating-point math, and it would not make sense to try and put + // actual bounds on this here because it depends on the image aspect ratio + // which can get pretty extreme. + // STBIR_ASSERT(stbir__filter_info_table[filter].kernel( + // (float)(out_last_pixel + 1) + 0.5f - out_center_of_in, + // scale_ratio) == 0); for (i = out_last_pixel - out_first_pixel; i >= 0; i--) { if (coefficient_group[i]) break; @@ -851,7 +861,7 @@ static float* stbir__get_decode_buffer(stbir__info* stbir_info) { } #define STBIR__DECODE(type, colorspace) \ - ((type) * (STBIR_MAX_COLORSPACES) + (colorspace)) + ((int)(type) * (STBIR_MAX_COLORSPACES) + (int)(colorspace)) static void stbir__decode_scanline(stbir__info* stbir_info, int n) { int c; @@ -1199,7 +1209,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int out_pixel_index = k * 1; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; } @@ -1220,7 +1229,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int out_pixel_index = k * 2; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += @@ -1243,7 +1251,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int out_pixel_index = k * 3; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += @@ -1268,7 +1275,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, int out_pixel_index = k * 4; float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - STBIR_ASSERT(coefficient != 0); output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; output_buffer[out_pixel_index + 1] += diff --git a/third_party/stb/stb_image_write.c b/third_party/stb/stb_image_write.c index 000ebe7c49e1..92dbc9262be0 100644 --- a/third_party/stb/stb_image_write.c +++ b/third_party/stb/stb_image_write.c @@ -1,123 +1,21 @@ -/* stb_image_write - v1.13 - public domain - http://nothings.org/stb - * writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 - * no warranty implied; use at your own risk - * - * ABOUT: - * - * This file is a library for writing images to stdio or a callback. - * - * The PNG output is not optimal; it is 20-50% larger than the file - * written by a decent optimizing implementation; though providing a - * custom zlib compress function (see STBIW_ZLIB_COMPRESS) can - * mitigate that. This library is designed for source code - * compactness and simplicity, not optimal image file size or - * run-time performance. - * - * USAGE: - * - * There are five functions, one for each image file format: - * - * stbi_write_png - * stbi_write_bmp - * stbi_write_tga - * stbi_write_jpg - * stbi_write_hdr - * - * stbi_flip_vertically_on_write - * - * There are also five equivalent functions that use an arbitrary - * write function. You are expected to open/close your - * file-equivalent before and after calling these: - * - * stbi_write_png_to_func - * stbi_write_bmp_to_func - * stbi_write_tga_to_func - * stbi_write_hdr_to_func - * stbi_write_jpg_to_func - * - * where the callback is: - * void stbi_write_func(void *context, void *data, int size); - * - * You can configure it with these: - * stbi_write_tga_with_rle - * stbi_write_png_compression_level - * stbi_write_force_png_filter - * - * Each function returns 0 on failure and non-0 on success. - * - * The functions create an image file defined by the parameters. The - * image is a rectangle of pixels stored from left-to-right, - * top-to-bottom. Each pixel contains 'comp' channels of data stored - * interleaved with 8-bits per channel, in the following order: 1=Y, - * 2=YA, 3=RGB, 4=RGBA. (Y is monochrome color.) The rectangle is 'w' - * pixels wide and 'h' pixels tall. The *data pointer points to the - * first byte of the top-left-most pixel. For PNG, "stride_in_bytes" - * is the distance in bytes from the first byte of a row of pixels to - * the first byte of the next row of pixels. - * - * PNG creates output files with the same number of components as the - * input. The BMP format expands Y to RGB in the file format and does - * not output alpha. - * - * PNG supports writing rectangles of data even when the bytes - * storing rows of data are not consecutive in memory (e.g. - * sub-rectangles of a larger image), by supplying the stride between - * the beginning of adjacent rows. The other formats do not. (Thus - * you cannot write a native-format BMP through the BMP writer, both - * because it is in BGR order and because it may have padding at the - * end of the line.) - * - * PNG allows you to set the deflate compression level by setting the - * global variable 'stbi_write_png_compression_level' (it defaults to - * 8). - * - * HDR expects linear float data. Since the format is always 32-bit - * rgb(e) data, alpha (if provided) is discarded, and for monochrome - * data it is replicated across all three channels. - * - * TGA supports RLE or non-RLE compressed data. To use - * non-RLE-compressed data, set the global variable - * 'stbi_write_tga_with_rle' to 0. - * - * JPEG does ignore alpha channels in input data; quality is between - * 1 and 100. Higher quality looks better but results in a bigger - * image. JPEG baseline (no JPEG progressive). - * - * CREDITS: - * - * - * Sean Barrett - PNG/BMP/TGA - * Baldur Karlsson - HDR - * Jean-Sebastien Guay - TGA monochrome - * Tim Kelsey - misc enhancements - * Alan Hickman - TGA RLE - * Emmanuel Julien - initial file IO callback implementation - * Jon Olick - original jo_jpeg.cpp code - * Daniel Gibson - integrate JPEG, allow external zlib - * Aarni Koskela - allow choosing PNG filter - * - * bugfixes: - * github:Chribba - * Guillaume Chereau - * github:jry2 - * github:romigrou - * Sergio Gonzalez - * Jonas Karlsson - * Filip Wasil - * Thatcher Ulrich - * github:poppolopoppo - * Patrick Boettcher - * github:xeekworx - * Cap Petschulat - * Simon Rodriguez - * Ivan Tikhonov - * github:ignotion - * Adam Schackart - * - * LICENSE - * - * Public Domain (www.unlicense.org) - */ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2023 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ #include "third_party/stb/stb_image_write.h" #include "dsp/core/core.h" #include "libc/assert.h" @@ -132,17 +30,25 @@ #include "libc/str/str.h" #include "third_party/zlib/zlib.h" +asm(".ident\t\"\\n\\n\ +stb_image_write (Public Domain)\\n\ +Credit: Sean Barrett, et al.\\n\ +http://nothings.org/stb\""); + #define STBIW_UCHAR(x) (unsigned char)((x)&0xff) #define STBIW_REALLOC_SIZED(p, oldsz, newsz) realloc(p, newsz) typedef struct { stbi_write_func *func; void *context; + unsigned char buffer[64]; + int buf_used; } stbi__write_context; -int stbi__flip_vertically_on_write = 0; int stbi_write_tga_with_rle = 1; +static int stbi__flip_vertically_on_write = 0; + void stbi_flip_vertically_on_write(int flag) { stbi__flip_vertically_on_write = flag; } @@ -213,17 +119,31 @@ static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) { va_end(v); } +static void stbiw__write_flush(stbi__write_context *s) { + if (s->buf_used) { + s->func(s->context, &s->buffer, s->buf_used); + s->buf_used = 0; + } +} + static void stbiw__putc(stbi__write_context *s, unsigned char c) { s->func(s->context, &c, 1); } +static void stbiw__write1(stbi__write_context *s, unsigned char a) { + if ((size_t)s->buf_used + 1 > sizeof(s->buffer)) stbiw__write_flush(s); + s->buffer[s->buf_used++] = a; +} + static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { - unsigned char arr[3]; - arr[0] = a; - arr[1] = b; - arr[2] = c; - s->func(s->context, arr, 3); + int n; + if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) stbiw__write_flush(s); + n = s->buf_used; + s->buf_used = n + 3; + s->buffer[n + 0] = a; + s->buffer[n + 1] = b; + s->buffer[n + 2] = c; } static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, @@ -232,7 +152,7 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, unsigned char bg[3] = {255, 0, 255}, px[3]; int k; - if (write_alpha < 0) s->func(s->context, &d[comp - 1], 1); + if (write_alpha < 0) stbiw__write1(s, d[comp - 1]); switch (comp) { case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as @@ -241,7 +161,7 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, if (expand_mono) stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp else - s->func(s->context, d, 1); // monochrome TGA + stbiw__write1(s, d[0]); // monochrome TGA break; case 4: if (!write_alpha) { @@ -255,7 +175,7 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); break; } - if (write_alpha > 0) s->func(s->context, &d[comp - 1], 1); + if (write_alpha > 0) stbiw__write1(s, d[comp - 1]); } static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, @@ -278,6 +198,7 @@ static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, unsigned char *d = (unsigned char *)data + (j * x + i) * comp; stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); } + stbiw__write_flush(s); s->func(s->context, &zero, scanline_pad); } } @@ -301,24 +222,41 @@ static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { int pad = (-x * 3) & 3; - return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *)data, 0, pad, - "11 4 22 4" - "4 44 22 444444", - 'B', 'M', 14 + 40 + (x * 3 + pad) * y, 0, 0, - 14 + 40, // file header - 40, x, y, 1, 24, 0, 0, 0, 0, 0, 0); // bitmap header + + if (comp != 4) { + // write RGB bitmap + int pad = (-x * 3) & 3; + return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *)data, 0, pad, + "11 4 22 4" + "4 44 22 444444", + 'B', 'M', 14 + 40 + (x * 3 + pad) * y, 0, 0, + 14 + 40, // file header + 40, x, y, 1, 24, 0, 0, 0, 0, 0, 0); // bitmap header + } else { + // RGBA bitmaps need a v4 header + // use BI_BITFIELDS mode with 32bpp and alpha mask + // (straight BI_RGB with alpha mask doesn't work in most readers) + return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *)data, 1, 0, + "11 4 22 4" + "4 44 22 444444 4444 4 444 444 444 444", + 'B', 'M', 14 + 108 + x * y * 4, 0, 0, + 14 + 108, // file header + 108, x, y, 1, 32, 3, 0, 0, 0, 0, 0, 0xff0000, 0xff00, + 0xff, 0xff000000u, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0); // bitmap V4 header + } } int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { - stbi__write_context s; + stbi__write_context s = {0}; stbi__start_write_callbacks(&s, func, context); return stbi_write_bmp_core(&s, x, y, comp, data); } int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) { - stbi__write_context s; + stbi__write_context s = {0}; if (stbi__start_write_file(&s, filename)) { int r = stbi_write_bmp_core(&s, x, y, comp, data); stbi__end_write_file(&s); @@ -394,31 +332,32 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, if (diff) { unsigned char header = STBIW_UCHAR(len - 1); - s->func(s->context, &header, 1); + stbiw__write1(s, header); for (k = 0; k < len; ++k) { stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); } } else { unsigned char header = STBIW_UCHAR(len - 129); - s->func(s->context, &header, 1); + stbiw__write1(s, header); stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); } } } + stbiw__write_flush(s); } return 1; } int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { - stbi__write_context s; + stbi__write_context s = {0}; stbi__start_write_callbacks(&s, func, context); return stbi_write_tga_core(&s, x, y, comp, (void *)data); } int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) { - stbi__write_context s; + stbi__write_context s = {0}; if (stbi__start_write_file(&s, filename)) { int r = stbi_write_tga_core(&s, x, y, comp, (void *)data); stbi__end_write_file(&s); @@ -473,24 +412,25 @@ static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { } static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, - int *bitCnt, float *CDU, float *fdtbl, int DC, + int *bitCnt, float *CDU, unsigned du_stride, + float *fdtbl, int DC, const unsigned short HTDC[256][2], const unsigned short HTAC[256][2]) { const unsigned short EOB[2] = {HTAC[0x00][0], HTAC[0x00][1]}; const unsigned short M16zeroes[2] = {HTAC[0xF0][0], HTAC[0xF0][1]}; - unsigned i, diff, end0pos; + unsigned i, j, diff, end0pos, x, y; int DU[64]; - dctjpeg((void *)CDU); + dctjpeg((void *)CDU, du_stride / 8); // Quantize/descale/zigzag the coefficients - for (i = 0; i < 64; ++i) { - float v = CDU[i] * fdtbl[i]; - DU[stbiw__jpg_ZigZag[i]] = v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f); - // DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + - // 0.5f)); ceilf() and floorf() are C99, not C89, but I /think/ they're not - // needed here anyway? - /* DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? v - 0.5f : v + 0.5f); */ + for (j = 0, y = 0; y < 8; ++y) { + for (x = 0; x < 8; ++x, ++j) { + float v; + i = y * du_stride + x; + v = CDU[i] * fdtbl[j]; + DU[stbiw__jpg_ZigZag[j]] = v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f); + } } // Encode DC @@ -710,7 +650,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f}; - int row, col, i, k; + int row, col, i, k, subsample; float fdtbl_Y[64], fdtbl_UV[64]; unsigned char YTable[64], UVTable[64]; @@ -719,6 +659,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, } quality = quality ? quality : 97; + subsample = quality <= 97 ? 1 : 0; quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; quality = quality < 50 ? 5000 / quality : 200 - quality * 2; @@ -759,7 +700,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, STBIW_UCHAR(width), 3, 1, - 0x11, + (unsigned char)(subsample ? 0x22 : 0x11), 0, 2, 0x11, @@ -803,42 +744,92 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, // Encode 8x8 macroblocks { static const unsigned short fillBits[] = {0x7F, 7}; - const unsigned char *imageData = (const unsigned char *)data; int DCY = 0, DCU = 0, DCV = 0; int bitBuf = 0, bitCnt = 0; // comp == 2 is grey+alpha (alpha is ignored) int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + const unsigned char *dataR = (const unsigned char *)data; + const unsigned char *dataG = dataR + ofsG; + const unsigned char *dataB = dataR + ofsB; int x, y, pos; - for (y = 0; y < height; y += 8) { - for (x = 0; x < width; x += 8) { - float YDU[64], UDU[64], VDU[64]; - for (row = y, pos = 0; row < y + 8; ++row) { - // row >= height => use last input row - int clamped_row = (row < height) ? row : height - 1; - int base_p = - (stbi__flip_vertically_on_write ? (height - 1 - clamped_row) - : clamped_row) * - width * comp; - for (col = x; col < x + 8; ++col, ++pos) { - float r, g, b; - // if col >= width => use pixel from last input column - int p = base_p + ((col < width) ? col : (width - 1)) * comp; - - r = imageData[p + 0]; - g = imageData[p + ofsG]; - b = imageData[p + ofsB]; - YDU[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128; - UDU[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b; - VDU[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b; + if (subsample) { + for (y = 0; y < height; y += 16) { + for (x = 0; x < width; x += 16) { + float Y[256], U[256], V[256]; + for (row = y, pos = 0; row < y + 16; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = + (stbi__flip_vertically_on_write ? (height - 1 - clamped_row) + : clamped_row) * + width * comp; + for (col = x; col < x + 16; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width - 1)) * comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128; + U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b; + V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 0, 16, fdtbl_Y, + DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 8, 16, fdtbl_Y, + DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 128, 16, fdtbl_Y, + DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 136, 16, fdtbl_Y, + DCY, YDC_HT, YAC_HT); + + // subsample U,V + { + float subU[64], subV[64]; + int yy, xx; + for (yy = 0, pos = 0; yy < 8; ++yy) { + for (xx = 0; xx < 8; ++xx, ++pos) { + int j = yy * 32 + xx * 2; + subU[pos] = + (U[j + 0] + U[j + 1] + U[j + 16] + U[j + 17]) * 0.25f; + subV[pos] = + (V[j + 0] + V[j + 1] + V[j + 16] + V[j + 17]) * 0.25f; + } + } + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, + DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, + DCV, UVDC_HT, UVAC_HT); } } + } + } else { + for (y = 0; y < height; y += 8) { + for (x = 0; x < width; x += 8) { + float Y[64], U[64], V[64]; + for (row = y, pos = 0; row < y + 8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = + (stbi__flip_vertically_on_write ? (height - 1 - clamped_row) + : clamped_row) * + width * comp; + for (col = x; col < x + 8; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width - 1)) * comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128; + U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b; + V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b; + } + } - DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, - YDC_HT, YAC_HT); - DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, - UVDC_HT, UVAC_HT); - DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, - UVDC_HT, UVAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, + YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, + UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, + UVDC_HT, UVAC_HT); + } } } @@ -855,14 +846,14 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) { - stbi__write_context s; + stbi__write_context s = {0}; stbi__start_write_callbacks(&s, func, context); return stbi_write_jpg_core(&s, x, y, comp, (void *)data, quality); } int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) { - stbi__write_context s; + stbi__write_context s = {0}; if (stbi__start_write_file(&s, filename)) { int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); stbi__end_write_file(&s); @@ -1027,14 +1018,14 @@ static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) { - stbi__write_context s; + stbi__write_context s = {0}; stbi__start_write_callbacks(&s, func, context); return stbi_write_hdr_core(&s, x, y, comp, (float *)data); } int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { - stbi__write_context s; + stbi__write_context s = {0}; if (stbi__start_write_file(&s, filename)) { int r = stbi_write_hdr_core(&s, x, y, comp, (float *)data); stbi__end_write_file(&s); diff --git a/third_party/stb/stb_image_write.h b/third_party/stb/stb_image_write.h index 69ab97401309..ad312062481f 100644 --- a/third_party/stb/stb_image_write.h +++ b/third_party/stb/stb_image_write.h @@ -4,7 +4,6 @@ COSMOPOLITAN_C_START_ extern int stbi_write_png_compression_level; -extern int stbi__flip_vertically_on_write; extern int stbi_write_tga_with_rle; extern int stbi_write_force_png_filter; diff --git a/third_party/stb/stb_image_write_png.c b/third_party/stb/stb_image_write_png.c index b327d1583505..7db6b75fbe0e 100644 --- a/third_party/stb/stb_image_write_png.c +++ b/third_party/stb/stb_image_write_png.c @@ -181,8 +181,8 @@ static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, mymap = (y != 0) ? mapping : firstmap; type = mymap[filter_type]; z = pixels + - stride_bytes * (stbi__flip_vertically_on_write ? height - 1 - y : y); - signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + stride_bytes * (stbi_flip_vertically_on_write ? height - 1 - y : y); + signed_stride = stbi_flip_vertically_on_write ? -stride_bytes : stride_bytes; if (type == 0) { memcpy(line_buffer, z, width * n); diff --git a/third_party/stb/stb_rect_pack.c b/third_party/stb/stb_rect_pack.c index ae5aa7475a18..95fd7ddbba3d 100644 --- a/third_party/stb/stb_rect_pack.c +++ b/third_party/stb/stb_rect_pack.c @@ -42,8 +42,6 @@ asm(".include \"libc/disclaimer.inc\""); // Useful for e.g. packing rectangular textures into an atlas. // Does not do rotation. // -// in the file that you want to have the implementation. -// // Not necessarily the awesomest packing method, but better than // the totally naive one in stb_truetype (which is primarily what // this is meant to replace). @@ -391,7 +389,11 @@ static int rect_height_compare(const void *a, const void *b) return -1; if (p->h < q->h) return 1; - return (p->w > q->w) ? -1 : (p->w < q->w); + if (p->w > q->w) + return -1; + if (p->w < q->w) + return 1; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); } static int rect_original_order(const void *a, const void *b) diff --git a/third_party/stb/stb_vorbis.c b/third_party/stb/stb_vorbis.c index 283d7c1a8238..8d2beee93c30 100644 --- a/third_party/stb/stb_vorbis.c +++ b/third_party/stb/stb_vorbis.c @@ -1,4 +1,4 @@ -// Ogg Vorbis audio decoder - v1.17 - public domain +// Ogg Vorbis audio decoder - v1.22 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. @@ -26,12 +26,16 @@ // Terje Mathisen Niklas Frykholm Andy Hill // Casey Muratori John Bolton Gargaj // Laurent Gomila Marc LeBlanc Ronny Chevalier -// Bernhard Wodo Evan Balster alxprd@github +// Bernhard Wodo Evan Balster github:alxprd // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart -// manxorist@github saga musix github:infatum -// Timur Gagiev Maxwell Koo +// github:manxorist Saga Musix github:infatum +// Timur Gagiev Maxwell Koo Peter Waller +// github:audinowho Dougall Johnson David Reid +// github:Clownacy Pedro J. Estebanez Remi Verschelde +// AnthoFoxo github:morlat Gabriel Ravier // +#include "third_party/stb/stb_vorbis.h" #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/fmt/conv.h" @@ -42,7 +46,6 @@ #include "libc/mem/alloca.h" #include "libc/mem/mem.h" #include "libc/str/str.h" -#include "third_party/stb/stb_vorbis.h" // STB_VORBIS_NO_PUSHDATA_API // does not compile the code for the various stb_vorbis_*_pushdata() @@ -342,6 +345,10 @@ struct stb_vorbis { unsigned int temp_memory_required; unsigned int setup_temp_memory_required; + char *vendor; + int comment_list_length; + char **comment_list; + // input config #ifndef STB_VORBIS_NO_STDIO FILE *f; @@ -357,8 +364,11 @@ struct stb_vorbis { uint8 push_mode; + // the page to seek to when seeking to start, may be zero uint32 first_audio_page_offset; + // p_first is the page on which the first audio packet ends + // (but not necessarily the page on which it starts) ProbedPage p_first, p_last; // memory management @@ -492,7 +502,7 @@ static dontinline void *make_block_array(void *mem, int count, int size) { } static dontinline void *setup_malloc(vorb *f, int sz) { - sz = (sz + 3) & ~3; + sz = (sz + 7) & ~7; // round up to nearest 8 for alignment of future allocs. f->setup_memory_required += sz; if (f->alloc.alloc_buffer) { void *p = (char *)f->alloc.alloc_buffer + f->setup_offset; @@ -509,7 +519,7 @@ static dontinline void setup_free(vorb *f, void *p) { } static dontinline void *setup_temp_malloc(vorb *f, int sz) { - sz = (sz + 3) & ~3; + sz = (sz + 7) & ~7; // round up to nearest 8 for alignment of future allocs. if (f->alloc.alloc_buffer) { if (f->temp_offset - sz < f->setup_offset) return NULL; f->temp_offset -= sz; @@ -520,7 +530,7 @@ static dontinline void *setup_temp_malloc(vorb *f, int sz) { static dontinline void setup_temp_free(vorb *f, void *p, int sz) { if (f->alloc.alloc_buffer) { - f->temp_offset += (sz + 3) & ~3; + f->temp_offset += (sz + 7) & ~7; return; } free(p); @@ -592,7 +602,7 @@ static float float32_unpack(uint32 x) { uint32 sign = x & 0x80000000; uint32 exp = (x & 0x7fe00000) >> 21; double res = sign ? -(double)mantissa : (double)mantissa; - return (float)ldexp((float)res, exp - 788); + return (float)ldexp((float)res, (int)exp - 788); } // zlib & jpeg huffman tables assume that the output symbols @@ -624,6 +634,8 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) { assert(c->sorted_entries == 0); return TRUE; } + // no error return required, code reading lens checks this + assert(len[k] < 32); // add to the list add_entry(c, 0, k, m++, len[k], values); // add all available leaves @@ -636,6 +648,8 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) { uint32 res; int z = len[i], y; if (z == NO_CODE) continue; + // no error return required, code reading lens checks this + assert(z < 32); // find lowest available leaf (should always be earliest, // which is what the specification calls for) // note that this property, and the fact we can never have @@ -647,12 +661,10 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) { return FALSE; } res = available[z]; - assert(z >= 0 && z < 32); available[z] = 0; add_entry(c, _bitreverse32(res), i, m++, len[i], values); // propagate availability up the tree if (z != len[i]) { - assert(len[i] >= 0 && len[i] < 32); for (y = len[i]; y > z; --y) { assert(available[y] == 0); available[y] = res + (1 << (32 - y)); @@ -979,6 +991,9 @@ static int capture_pattern(vorb *f) { static int start_page_no_capturepattern(vorb *f) { uint32 loc0, loc1, n; + if (f->first_decode && !IS_PUSH_MODE(f)) { + f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; + } // stream structure version if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); // header flag @@ -1015,14 +1030,12 @@ static int start_page_no_capturepattern(vorb *f) { } if (f->first_decode) { int i, len; - ProbedPage p; len = 0; for (i = 0; i < f->segment_count; ++i) len += f->segments[i]; len += 27 + f->segment_count; - p.page_start = f->first_audio_page_offset; - p.page_end = p.page_start + len; - p.last_decoded_sample = loc0; - f->p_first = p; + + f->p_first.page_end = f->p_first.page_start + len; + f->p_first.last_decoded_sample = loc0; } f->next_seg = 0; return TRUE; @@ -1112,6 +1125,15 @@ static int get8_packet(vorb *f) { return x; } +static int get32_packet(vorb *f) { + uint32 x; + x = get8_packet(f); + x += (uint32)get8_packet(f) << 8; + x += (uint32)get8_packet(f) << 16; + x += (uint32)get8_packet(f) << 24; + return x; +} + static void flush_packet(vorb *f) { while (get8_packet_raw(f) != EOP) ; @@ -1141,7 +1163,7 @@ static uint32 get_bits(vorb *f, int n) { f->valid_bits += 8; } } - if (f->valid_bits < 0) return 0; + assert(f->valid_bits >= n); z = f->acc & ((1 << n) - 1); f->acc >>= n; f->valid_bits -= n; @@ -1213,7 +1235,7 @@ static int codebook_decode_scalar_raw(vorb *f, Codebook *c) { assert(!c->sparse); for (i = 0; i < c->entries; ++i) { if (c->codeword_lengths[i] == NO_CODE) continue; - if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i]) - 1))) { + if (c->codewords[i] == (f->acc & ((1u << c->codeword_lengths[i]) - 1))) { if (f->valid_bits >= c->codeword_lengths[i]) { f->acc >>= c->codeword_lengths[i]; f->valid_bits -= c->codeword_lengths[i]; @@ -1402,7 +1424,8 @@ static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), // and the length we'll be using (effective) if (c_inter + p_inter * ch + effective > len * ch) { - effective = len * ch - (p_inter * ch - c_inter); + // https://github.com/nothings/stb/pull/1490 + effective = len * ch - (p_inter * ch + c_inter); } #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK @@ -1705,49 +1728,7 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, ++class_set; #endif } - } else if (ch == 1) { - while (pcount < part_read) { - int z = r->begin + pcount * r->part_size; - int c_inter = 0, p_inter = z; - if (pass_ == 0) { - Codebook *c = f->codebooks + r->classbook; - int q; - DECODE(q, f, c); - if (q == EOP) goto done; -#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[0][class_set] = r->classdata[q]; -#else - for (i = classwords - 1; i >= 0; --i) { - classifications[0][i + pcount] = q % r->classifications; - q /= r->classifications; - } -#endif - } - for (i = 0; i < classwords && pcount < part_read; ++i, ++pcount) { - int z = r->begin + pcount * r->part_size; -#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[0][class_set][i]; -#else - int c = classifications[0][pcount]; -#endif - int b = r->residue_books[c][pass_]; - if (b >= 0) { - Codebook *book = f->codebooks + b; - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, - ch, &c_inter, &p_inter, - n, r->part_size)) - goto done; - } else { - z += r->part_size; - c_inter = 0; - p_inter = z; - } - } -#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; -#endif - } - } else { + } else if (ch > 2) { while (pcount < part_read) { int z = r->begin + pcount * r->part_size; int c_inter = z % ch, p_inter = z / ch; @@ -2153,34 +2134,33 @@ static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, while (z > base) { float k00, k11; + float l00, l11; k00 = z[-0] - z[-8]; k11 = z[-1] - z[-9]; + l00 = z[-2] - z[-10]; + l11 = z[-3] - z[-11]; z[-0] = z[-0] + z[-8]; z[-1] = z[-1] + z[-9]; - z[-8] = k00; - z[-9] = k11; - - k00 = z[-2] - z[-10]; - k11 = z[-3] - z[-11]; z[-2] = z[-2] + z[-10]; z[-3] = z[-3] + z[-11]; - z[-10] = (k00 + k11) * A2; - z[-11] = (k11 - k00) * A2; + z[-8] = k00; + z[-9] = k11; + z[-10] = (l00 + l11) * A2; + z[-11] = (l11 - l00) * A2; - k00 = z[-12] - z[-4]; // reverse to avoid a unary negation + k00 = z[-4] - z[-12]; k11 = z[-5] - z[-13]; + l00 = z[-6] - z[-14]; + l11 = z[-7] - z[-15]; z[-4] = z[-4] + z[-12]; z[-5] = z[-5] + z[-13]; - z[-12] = k11; - z[-13] = k00; - - k00 = z[-14] - z[-6]; // reverse to avoid a unary negation - k11 = z[-7] - z[-15]; z[-6] = z[-6] + z[-14]; z[-7] = z[-7] + z[-15]; - z[-14] = (k00 + k11) * A2; - z[-15] = (k00 - k11) * A2; + z[-12] = k11; + z[-13] = -k00; + z[-14] = (l11 - l00) * A2; + z[-15] = (l00 + l11) * -A2; iter_54(z); iter_54(z - 8); @@ -2618,7 +2598,8 @@ void inverse_mdct_naive(float *buffer, int n) #endif static float *get_window(vorb *f, int len) { - len <<= 1; + // https://github.com/nothings/stb/pull/1499 + len = (unsigned int)len << 1; if (len == f->blocksize_0) return f->window[0]; if (len == f->blocksize_1) return f->window[1]; return NULL; @@ -2643,6 +2624,7 @@ static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, for (q = 1; q < g->values; ++q) { j = g->sorted_order[q]; #ifndef STB_VORBIS_NO_DEFER_FLOOR + // todo STBV_NOTUSED(step2_flag); if (finalY[j] >= 0) #else if (step2_flag[j]) @@ -2743,11 +2725,12 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int right_end, int *p_left) { Mapping *map; int i, j, k, n, n2; - int zero_channel[256]; - int really_zero_channel[256]; + int zero_channel[256] = {0}; + int really_zero_channel[256] = {0}; // WINDOWING + // todo STBV_NOTUSED(left_end); n = f->blocksize[m->blockflag]; map = &f->mapping[m->mapping]; @@ -2947,7 +2930,9 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, // this isn't to spec, but spec would require us to read ahead // and decode the size of all current frames--could be done, // but presumably it's not a commonly used feature - f->current_loc = -n2; // start of first frame is positioned for discard + f->current_loc = 0u - n2; // start of first frame is positioned for discard + // (NB this is an intentional unsigned + // overflow wrap-around) // we might have to discard samples "from" the next frame too, // if we're lapping a large block then a small at the start? f->discard_samples_deferred = n - right_end; @@ -3077,7 +3062,7 @@ static int vorbis_pump_first_frame(stb_vorbis *f) { } #ifndef STB_VORBIS_NO_PUSHDATA_API -static int is_whole_packet_present(stb_vorbis *f, int end_page) { +static int is_whole_packet_present(stb_vorbis *f) { // make sure that we have the packet available before continuing... // this requires a full ogg parse, but we know we can fetch from f->stream @@ -3097,8 +3082,6 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page) { break; } // either this continues, or it ends it... - if (end_page) - if (s < f->segment_count - 1) return error(f, VORBIS_invalid_stream); if (s == f->segment_count) s = -1; // set 'crosses page' flag if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; @@ -3132,8 +3115,6 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page) { p += q[s]; if (q[s] < 255) break; } - if (end_page) - if (s < n - 1) return error(f, VORBIS_invalid_stream); if (s == n) s = -1; // set 'crosses page' flag if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; @@ -3148,6 +3129,7 @@ static int start_decoder(vorb *f) { int longest_floorlist = 0; // first page, first packet + f->first_decode = TRUE; if (!start_page(f)) return FALSE; // validate page flag @@ -3206,6 +3188,50 @@ static int start_decoder(vorb *f) { if (!start_page(f)) return FALSE; if (!start_packet(f)) return FALSE; + + if (!next_segment(f)) return FALSE; + + if (get8_packet(f) != VORBIS_packet_comment) + return error(f, VORBIS_invalid_setup); + + for (i = 0; i < 6; ++i) header[i] = get8_packet(f); + + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + // file vendor + len = get32_packet(f); + f->vendor = (char *)setup_malloc(f, sizeof(char) * (len + 1)); + if (f->vendor == NULL) return error(f, VORBIS_outofmem); + for (i = 0; i < len; ++i) { + f->vendor[i] = get8_packet(f); + } + f->vendor[len] = (char)'\0'; + // user comments + f->comment_list_length = get32_packet(f); + f->comment_list = NULL; + if (f->comment_list_length > 0) { + f->comment_list = + (char **)setup_malloc(f, sizeof(char *) * (f->comment_list_length)); + if (f->comment_list == NULL) return error(f, VORBIS_outofmem); + } + + for (i = 0; i < f->comment_list_length; ++i) { + len = get32_packet(f); + f->comment_list[i] = (char *)setup_malloc(f, sizeof(char) * (len + 1)); + if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem); + + for (j = 0; j < len; ++j) { + f->comment_list[i][j] = get8_packet(f); + } + f->comment_list[i][len] = (char)'\0'; + } + + // framing_flag + x = get8_packet(f); + if (!(x & 1)) return error(f, VORBIS_invalid_setup); + + skip(f, f->bytes_in_seg); + f->bytes_in_seg = 0; + do { len = next_segment(f); skip(f, len); @@ -3217,7 +3243,7 @@ static int start_decoder(vorb *f) { #ifndef STB_VORBIS_NO_PUSHDATA_API if (IS_PUSH_MODE(f)) { - if (!is_whole_packet_present(f, TRUE)) { + if (!is_whole_packet_present(f)) { // convert error in ogg header to write type if (f->error == VORBIS_invalid_stream) f->error = VORBIS_invalid_setup; return FALSE; @@ -3290,7 +3316,10 @@ static int start_decoder(vorb *f) { if (present) { lengths[j] = get_bits(f, 5) + 1; ++total; - if (lengths[j] == 32) return error(f, VORBIS_invalid_setup); + if (lengths[j] == 32) { + if (c->sparse) setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_invalid_setup); + } } else { lengths[j] = NO_CODE; } @@ -3303,7 +3332,10 @@ static int start_decoder(vorb *f) { f->setup_temp_memory_required = c->entries; c->codeword_lengths = (uint8 *)setup_malloc(f, c->entries); - if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); + if (c->codeword_lengths == NULL) { + setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_outofmem); + } memcpy(c->codeword_lengths, lengths, c->entries); setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been @@ -3337,13 +3369,22 @@ static int start_decoder(vorb *f) { unsigned int size; if (c->sorted_entries) { c->codeword_lengths = (uint8 *)setup_malloc(f, c->sorted_entries); - if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + if (!c->codeword_lengths) { + setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_outofmem); + } c->codewords = (uint32 *)setup_temp_malloc( f, sizeof(*c->codewords) * c->sorted_entries); - if (!c->codewords) return error(f, VORBIS_outofmem); + if (!c->codewords) { + setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_outofmem); + } values = (uint32 *)setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); - if (!values) return error(f, VORBIS_outofmem); + if (!values) { + setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_outofmem); + } } size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; @@ -3352,7 +3393,10 @@ static int start_decoder(vorb *f) { } if (!compute_codewords(c, lengths, c->entries, values)) { - if (c->sparse) setup_temp_free(f, values, 0); + if (c->sparse) { + setup_temp_free(f, values, 0); + setup_temp_free(f, lengths, c->entries); + } return error(f, VORBIS_invalid_setup); } @@ -3360,12 +3404,18 @@ static int start_decoder(vorb *f) { // allocate an extra slot for sentinels c->sorted_codewords = (uint32 *)setup_malloc( f, sizeof(*c->sorted_codewords) * (c->sorted_entries + 1)); - if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); + if (c->sorted_codewords == NULL) { + if (c->sparse) setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_outofmem); + } // allocate an extra slot at the front so that c->sorted_values[-1] is // defined so that we can catch that case without an extra if c->sorted_values = (int *)setup_malloc( f, sizeof(*c->sorted_values) * (c->sorted_entries + 1)); - if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); + if (c->sorted_values == NULL) { + if (c->sparse) setup_temp_free(f, lengths, c->entries); + return error(f, VORBIS_outofmem); + } ++c->sorted_values; c->sorted_values[-1] = -1; compute_sorted_huffman(c, lengths, values); @@ -3434,8 +3484,7 @@ static int start_decoder(vorb *f) { unsigned int div = 1; for (k = 0; k < c->dimensions; ++k) { int off = (z / div) % c->lookup_values; - float val = mults[off]; - val = mults[off] * c->delta_value + c->minimum_value + last; + float val = mults[off] * c->delta_value + c->minimum_value + last; c->multiplicands[j * c->dimensions + k] = val; if (c->sequence_p) last = val; if (k + 1 < c->dimensions) { @@ -3520,7 +3569,7 @@ static int start_decoder(vorb *f) { return error(f, VORBIS_invalid_setup); } for (k = 0; k < 1 << g->class_subclasses[j]; ++k) { - g->subclass_books[j][k] = get_bits(f, 8) - 1; + g->subclass_books[j][k] = (int16)get_bits(f, 8) - 1; if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } @@ -3548,7 +3597,7 @@ static int start_decoder(vorb *f) { for (j = 0; j < g->values; ++j) g->sorted_order[j] = (uint8)p[j].id; // precompute the neighbors for (j = 2; j < g->values; ++j) { - int low, hi; + int low = 0, hi = 0; neighbors(g->Xlist, j, &low, &hi); g->neighbors[j][0] = low; g->neighbors[j][1] = hi; @@ -3726,7 +3775,9 @@ static int start_decoder(vorb *f) { int i, max_part_read = 0; for (i = 0; i < f->residue_count; ++i) { Residue *r = f->residue_config + i; - unsigned int actual_size = f->blocksize_1 / 2; + unsigned int rtype = f->residue_types[i]; + unsigned int actual_size = + rtype == 2 ? f->blocksize_1 : f->blocksize_1 / 2; unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; @@ -3749,8 +3800,6 @@ static int start_decoder(vorb *f) { f->temp_memory_required = imdct_mem; } - f->first_decode = TRUE; - if (f->alloc.alloc_buffer) { assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); // check if there's enough temp memory so we don't error later @@ -3759,13 +3808,30 @@ static int start_decoder(vorb *f) { return error(f, VORBIS_outofmem); } - f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point + // to a page without PAGEFLAG_continued_packet, so this either points + // to the first page, or the page after the end of the headers. It might + // be cleaner to point to a page in the middle of the headers, when that's + // the page where the first audio packet starts, but we'd have to also + // correctly skip the end of any continued packet in stb_vorbis_seek_start. + if (f->next_seg == -1) { + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + } else { + f->first_audio_page_offset = 0; + } return TRUE; } static void vorbis_deinit(stb_vorbis *p) { int i, j; + + setup_free(p, p->vendor); + for (i = 0; i < p->comment_list_length; ++i) { + setup_free(p, p->comment_list[i]); + } + setup_free(p, p->comment_list); + if (p->residue_config) { for (i = 0; i < p->residue_count; ++i) { Residue *r = p->residue_config + i; @@ -3828,8 +3894,7 @@ static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) { memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start if (z) { p->alloc = *z; - p->alloc.alloc_buffer_length_in_bytes = - (p->alloc.alloc_buffer_length_in_bytes + 3) & ~3; + p->alloc.alloc_buffer_length_in_bytes &= ~7; p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; } p->eof = 0; @@ -3861,6 +3926,14 @@ stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) { return d; } +stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) { + stb_vorbis_comment d; + d.vendor = f->vendor; + d.comment_list_length = f->comment_list_length; + d.comment_list = f->comment_list; + return d; +} + int stb_vorbis_get_error(stb_vorbis *f) { int e = f->error; f->error = VORBIS__no_error; @@ -3995,7 +4068,7 @@ int stb_vorbis_decode_frame_pushdata( f->error = VORBIS__no_error; // check that we have the entire packet in memory - if (!is_whole_packet_present(f, FALSE)) { + if (!is_whole_packet_present(f)) { *samples = 0; return 0; } @@ -4057,6 +4130,7 @@ stb_vorbis *stb_vorbis_open_pushdata( *error = VORBIS_need_more_data; else *error = p.error; + vorbis_deinit(&p); return NULL; } f = vorbis_alloc(&p); @@ -4109,7 +4183,7 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) { if (f->eof) return 0; if (header[4] != 0) goto invalid; goal = header[22] + (header[23] << 8) + (header[24] << 16) + - (header[25] << 24); + ((uint32)header[25] << 24); for (i = 22; i < 26; ++i) header[i] = 0; crc = 0; for (i = 0; i < 27; ++i) crc = crc32_update(crc, header[i]); @@ -4220,8 +4294,8 @@ static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) { static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { ProbedPage left, right, mid; int i, start_seg_with_known_loc, end_pos, page_start; - uint32 delta, stream_length, padding; - double offset, bytes_per_sample; + uint32 delta, stream_length, padding, last_sample_limit; + double offset = 0.0, bytes_per_sample = 0.0; int probe = 0; bytes_per_sample = 2; /* TODO(jart): ???? */ @@ -4237,9 +4311,9 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { // indicates should be the granule position (give or take one)). padding = ((f->blocksize_1 - f->blocksize_0) >> 2); if (sample_number < padding) - sample_number = 0; + last_sample_limit = 0; else - sample_number -= padding; + last_sample_limit = sample_number - padding; left = f->p_first; while (left.last_decoded_sample == ~0U) { @@ -4252,8 +4326,11 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { assert(right.last_decoded_sample != ~0U); // starting from the start is handled differently - if (sample_number <= left.last_decoded_sample) { - if (stb_vorbis_seek_start(f)) return 1; + if (last_sample_limit <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) { + if (f->current_loc > sample_number) return error(f, VORBIS_seek_failed); + return 1; + } return 0; } @@ -4272,10 +4349,10 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { bytes_per_sample = data_bytes / right.last_decoded_sample; offset = left.page_start + - bytes_per_sample * (sample_number - left.last_decoded_sample); + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); } else { // second probe (try to bound the other side) - double error = ((double)sample_number - mid.last_decoded_sample) * + double error = ((double)last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; if (error >= 0 && error < 8000) error = 8000; if (error < 0 && error > -8000) error = -8000; @@ -4306,13 +4383,15 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { } // if we've just found the last page again then we're in a tricky file, - // and we're close enough. - if (mid.page_start == right.page_start) break; - - if (sample_number < mid.last_decoded_sample) - right = mid; - else - left = mid; + // and we're close enough (if it wasn't an interpolation probe). + if (mid.page_start == right.page_start) { + if (probe >= 2 || delta <= 65536) break; + } else { + if (last_sample_limit < mid.last_decoded_sample) + right = mid; + else + left = mid; + } ++probe; } @@ -4425,8 +4504,8 @@ int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) { flush_packet(f); } } - // the next frame will start with the sample - assert(f->current_loc == sample_number); + // the next frame should start with the sample + if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed); return 1; } @@ -4502,7 +4581,8 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) { // set. whoops! break; } - previous_safe = last_page_loc + 1; + // NOTE: not used after this point, but note for debugging + // previous_safe = last_page_loc + 1; last_page_loc = stb_vorbis_get_file_offset(f); } @@ -4606,7 +4686,10 @@ stb_vorbis *stb_vorbis_open_filename(const char *filename, int *error, stb_vorbis *stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; - if (data == NULL) return NULL; + if (!data) { + if (error) *error = VORBIS_unexpected_eof; + return NULL; + } vorbis_init(&p, alloc); p.stream = (uint8 *)data; p.stream_end = (uint8 *)data + len; @@ -4672,18 +4755,18 @@ static void copy_samples(short *dest, float *src, int len) { for (i = 0; i < len; ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i], 15); - if ((unsigned int)(v + 32768) > 65535) v = v < 0 ? -32768 : 32767; + if (((unsigned int)v + 32768) > 65535) v = v < 0 ? -32768 : 32767; dest[i] = v; } } static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) { -#define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i, j, o, n = BUFFER_SIZE; +#define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i, j, o, n = STB_BUFFER_SIZE; check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE) { + for (o = 0; o < len; o += STB_BUFFER_SIZE) { memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j = 0; j < num_c; ++j) { @@ -4694,20 +4777,21 @@ static void compute_samples(int mask, short *output, int num_c, float **data, for (i = 0; i < n; ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp, buffer[i], 15); - if ((unsigned int)(v + 32768) > 65535) v = v < 0 ? -32768 : 32767; + if (((unsigned int)v + 32768) > 65535) v = v < 0 ? -32768 : 32767; output[o + i] = v; } } +#undef STB_BUFFER_SIZE } static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) { -#define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i, j, o, n = BUFFER_SIZE >> 1; +#define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i, j, o, n = STB_BUFFER_SIZE >> 1; // o is the offset in the source data check_endianness(); - for (o = 0; o < len; o += (BUFFER_SIZE >> 1)) { + for (o = 0; o < len; o += (STB_BUFFER_SIZE >> 1)) { // o2 is the offset in the output data int o2 = o << 1; memset(buffer, 0, sizeof(buffer)); @@ -4732,10 +4816,11 @@ static void compute_stereo_samples(short *output, int num_c, float **data, for (i = 0; i < (n << 1); ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp, buffer[i], 15); - if ((unsigned int)(v + 32768) > 65535) v = v < 0 ? -32768 : 32767; + if (((unsigned int)v + 32768) > 65535) v = v < 0 ? -32768 : 32767; output[o2 + i] = v; } } +#undef STB_BUFFER_SIZE } static void convert_samples_short(int buf_c, short **buffer, int b_offset, @@ -4759,7 +4844,7 @@ static void convert_samples_short(int buf_c, short **buffer, int b_offset, int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) { - float **output; + float **output = NULL; int len = stb_vorbis_get_frame_float(f, NULL, &output); if (len > num_samples) len = num_samples; if (len) convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); @@ -4784,7 +4869,7 @@ static void convert_channels_short_interleaved(int buf_c, short *buffer, float f = data[i][d_offset + j]; int v = FAST_SCALED_FLOAT_TO_INT(temp, f, 15); // data[i][d_offset+j],15); - if ((unsigned int)(v + 32768) > 65535) v = v < 0 ? -32768 : 32767; + if (((unsigned int)v + 32768) > 65535) v = v < 0 ? -32768 : 32767; *buffer++ = v; } for (; i < buf_c; ++i) *buffer++ = 0; @@ -4812,8 +4897,6 @@ int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, float **outputs; int len = num_shorts / channels; int n = 0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n + k >= len) k = len - n; @@ -4834,8 +4917,6 @@ int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) { float **outputs; int n = 0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n + k >= len) k = len - n; diff --git a/third_party/stb/stb_vorbis.h b/third_party/stb/stb_vorbis.h index 8dd54975bc03..6d4bd58f5ace 100644 --- a/third_party/stb/stb_vorbis.h +++ b/third_party/stb/stb_vorbis.h @@ -44,9 +44,18 @@ typedef struct { int max_frame_size; } stb_vorbis_info; +typedef struct { + char *vendor; + int comment_list_length; + char **comment_list; +} stb_vorbis_comment; + // get general information about the file stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); +// get ogg comments +stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); + // get the last error detected (clears it, too) int stb_vorbis_get_error(stb_vorbis *f); @@ -120,6 +129,12 @@ int stb_vorbis_decode_frame_pushdata( // channel. In other words, (*output)[0][0] contains the first sample from // the first channel, and (*output)[1][0] contains the first sample from // the second channel. +// +// *output points into stb_vorbis's internal output buffer storage; these +// buffers are owned by stb_vorbis and application code should not free +// them or modify their contents. They are transient and will be overwritten +// once you ask for more data to get decoded, so be sure to grab any data +// you need before then. void stb_vorbis_flush_pushdata(stb_vorbis *f); // inform stb_vorbis that your next datablock will not be contiguous with diff --git a/tool/viz/derasterize.c b/tool/viz/derasterize.c index f40c586692e2..5281eb344b95 100644 --- a/tool/viz/derasterize.c +++ b/tool/viz/derasterize.c @@ -552,8 +552,8 @@ static int ParseNumberOption(const char *arg) { return x; } -static void PrintUsage(int rc, FILE *f) { - fputs(HELPTEXT, f); +static void PrintUsage(int rc, int fd) { + tinyprint(fd, HELPTEXT, NULL); exit(rc); } @@ -574,9 +574,12 @@ static void GetOpts(int argc, char *argv[]) { break; case '?': case 'H': - PrintUsage(EXIT_SUCCESS, stdout); default: - PrintUsage(EX_USAGE, stderr); + if (opt == optopt) { + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); + } else { + PrintUsage(EX_USAGE, STDERR_FILENO); + } } } } diff --git a/tool/viz/memzoom.c b/tool/viz/memzoom.c index f72da79a64d3..5ce16f65b16f 100644 --- a/tool/viz/memzoom.c +++ b/tool/viz/memzoom.c @@ -45,6 +45,7 @@ #include "libc/str/unicode.h" #include "libc/sysv/consts/ex.h" #include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/poll.h" @@ -63,7 +64,7 @@ DESCRIPTION\n\ \n\ FLAGS\n\ \n\ - -h help\n\ + -h or -? help\n\ -z zoom\n\ -m morton ordering\n\ -H hilbert ordering\n\ @@ -888,10 +889,8 @@ static void MemZoom(void) { } while (!(action & INTERRUPTED)); } -static wontreturn void PrintUsage(int rc) { - Write("SYNOPSIS\n\n "); - Write(program_invocation_name); - Write(USAGE); +static wontreturn void PrintUsage(int rc, int fd) { + tinyprint(fd, "SYNOPSIS\n\n ", program_invocation_name, USAGE); exit(rc); } @@ -899,7 +898,7 @@ static void GetOpts(int argc, char *argv[]) { int opt; char *p; fps = 10; - while ((opt = getopt(argc, argv, "hzHNWf:p:")) != -1) { + while ((opt = getopt(argc, argv, "?hmzHNWf:p:")) != -1) { switch (opt) { case 'z': ++zoom; @@ -928,9 +927,13 @@ static void GetOpts(int argc, char *argv[]) { } break; case 'h': - PrintUsage(EXIT_SUCCESS); + case '?': default: - PrintUsage(EX_USAGE); + if (opt == optopt) { + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); + } else { + PrintUsage(EX_USAGE, STDERR_FILENO); + } } } if (pid) { @@ -942,10 +945,10 @@ static void GetOpts(int argc, char *argv[]) { stpcpy(p, "/maps"); } else { if (optind == argc) { - PrintUsage(EX_USAGE); + PrintUsage(EX_USAGE, STDERR_FILENO); } if (!memccpy(path, argv[optind], '\0', sizeof(path))) { - PrintUsage(EX_USAGE); + PrintUsage(EX_USAGE, STDERR_FILENO); } } } diff --git a/tool/viz/od16.c b/tool/viz/od16.c index c5effae93dc1..4ca3b2ef60c3 100644 --- a/tool/viz/od16.c +++ b/tool/viz/od16.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/fmt/fmt.h" @@ -26,6 +27,7 @@ #include "libc/str/str.h" #include "libc/sysv/consts/ex.h" #include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/fileno.h" #include "third_party/getopt/getopt.internal.h" #define USAGE \ @@ -37,17 +39,15 @@ Flags:\n\ -c INT\n\ -w INT width (aka cols) [default 8]\n\ -o PATH output path [default -]\n\ - -h shows this information\n\ + -h or -? shows this information\n\ \n" static long width_; static FILE *in_, *out_; static char *inpath_, *outpath_; -void PrintUsage(int rc, FILE *f) { - fputs("Usage: ", f); - fputs(program_invocation_name, f); - fputs(USAGE, f); +void PrintUsage(int rc, int fd) { + tinyprint(fd, "Usage: ", program_invocation_name, USAGE, NULL); exit(rc); } @@ -64,11 +64,14 @@ void GetOpts(int *argc, char *argv[]) { case 'w': width_ = strtol(optarg, NULL, 0); break; - case '?': case 'h': - PrintUsage(EXIT_SUCCESS, stdout); + case '?': default: - PrintUsage(EX_USAGE, stderr); + if (opt == optopt) { + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); + } else { + PrintUsage(EX_USAGE, STDERR_FILENO); + } } } if (optind == *argc) { diff --git a/tool/viz/printansi.c b/tool/viz/printansi.c index ec82c33134d8..ca556f809312 100644 --- a/tool/viz/printansi.c +++ b/tool/viz/printansi.c @@ -72,8 +72,8 @@ static struct Flags { enum TtyQuantizationAlgorithm quant; } g_flags; -static wontreturn void PrintUsage(int rc, FILE *f) { - fprintf(f, "Usage: %s%s", program_invocation_name, "\ +static wontreturn void PrintUsage(int rc, int fd) { + tinyprint(fd, "Usage: ", program_invocation_name, "\ [FLAGS] [PATH]\n\ \n\ FLAGS\n\ @@ -87,7 +87,7 @@ EXAMPLES\n\ \n\ printansi.com -w80 -h40 logo.png\n\ \n\ -\n"); +\n", NULL); exit(rc); } @@ -108,7 +108,7 @@ static void GetOpts(int *argc, char *argv[]) { g_flags.blocks = IsWindows() ? kTtyBlocksCp437 : kTtyBlocksUnicode; if (*argc == 2 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0)) { - PrintUsage(EXIT_SUCCESS, stdout); + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); } while ((opt = getopt(*argc, argv, "?ivpfrtxads234o:w:h:")) != -1) { switch (opt) { @@ -163,9 +163,12 @@ static void GetOpts(int *argc, char *argv[]) { ++__log_level; break; case '?': - PrintUsage(EXIT_SUCCESS, stdout); default: - PrintUsage(EX_USAGE, stderr); + if (opt == optopt) { + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); + } else { + PrintUsage(EX_USAGE, STDERR_FILENO); + } } } if (optind == *argc) { diff --git a/tool/viz/printimage.c b/tool/viz/printimage.c index 7c0167c9c04a..e3762668e48e 100644 --- a/tool/viz/printimage.c +++ b/tool/viz/printimage.c @@ -66,8 +66,8 @@ static struct Flags { struct winsize g_winsize; -static wontreturn void PrintUsage(int rc, FILE *f) { - fprintf(f, "Usage: %s%s", program_invocation_name, "\ +static wontreturn void PrintUsage(int rc, int fd) { + tinyprint(fd, "Usage: ", program_invocation_name, "\ [FLAGS] [PATH]\n\ \n\ FLAGS\n\ @@ -94,7 +94,7 @@ FLAGS\n\ EXAMPLES\n\ \n\ printimage.com -sxd lemurs.jpg # 256-color dither unsharp\n\ -\n"); +\n", NULL); exit(rc); } @@ -114,7 +114,7 @@ static void GetOpts(int *argc, char *argv[]) { g_flags.blocks = IsWindows() ? kTtyBlocksCp437 : kTtyBlocksUnicode; if (*argc == 2 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0)) { - PrintUsage(EXIT_SUCCESS, stdout); + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); } while ((opt = getopt(*argc, argv, "?vpmfirtxads234o:w:h:")) != -1) { switch (opt) { @@ -170,9 +170,12 @@ static void GetOpts(int *argc, char *argv[]) { ++__log_level; break; case '?': - PrintUsage(EXIT_SUCCESS, stdout); default: - PrintUsage(EX_USAGE, stderr); + if (opt == optopt) { + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); + } else { + PrintUsage(EX_USAGE, STDERR_FILENO); + } } } g_winsize.ws_col = 80; @@ -437,7 +440,7 @@ int main(int argc, char *argv[]) { int i; ShowCrashReports(); GetOpts(&argc, argv); - if (optind == argc) PrintUsage(0, stdout); + if (optind == argc) PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); stbi_set_unpremultiply_on_load(true); for (i = optind; i < argc; ++i) { WithImageFile(argv[i], ProcessImage); diff --git a/tool/viz/printvideo.c b/tool/viz/printvideo.c index 42256d7d492c..0e9557d166a2 100644 --- a/tool/viz/printvideo.c +++ b/tool/viz/printvideo.c @@ -121,7 +121,7 @@ Flags & Keyboard Shortcuts:\n\ -v increases verbosity [flag]\n\ -L PATH redirects stderr to path [flag]\n\ -y yes to interactive prompts [flag]\n\ - -h shows this information [flag]\n\ + -h or -? shows this information [flag]\n\ UP/DOWN adjust volume [keyboard]\n\ CTRL+L redraw [keyboard]\n\ CTRL+Z suspend [keyboard]\n\ @@ -1357,10 +1357,8 @@ static bool CanPlayAudio(void) { } } -static void PrintUsage(int rc, FILE *f) { - fputs("Usage: ", f); - fputs(program_invocation_name, f); - fputs(USAGE, f); +static void PrintUsage(int rc, int fd) { + tinyprint(fd, "Usage: ", program_invocation_name, USAGE, NULL); exit(rc); } @@ -1382,12 +1380,15 @@ static void GetOpts(int argc, char *argv[]) { case 'Y': yonly_ = true; break; - case '?': case 'h': - PrintUsage(EXIT_SUCCESS, stdout); + case '?': default: if (!ProcessOptKey(opt)) { - PrintUsage(EX_USAGE, stderr); + if (opt == optopt) { + PrintUsage(EXIT_SUCCESS, STDOUT_FILENO); + } else { + PrintUsage(EX_USAGE, STDERR_FILENO); + } } } } @@ -1544,7 +1545,7 @@ int main(int argc, char *argv[]) { fullclear_ = true; GetOpts(argc, argv); if (!tuned_) PickDefaults(); - if (optind == argc) PrintUsage(EX_USAGE, stderr); + if (optind == argc) PrintUsage(EX_USAGE, STDERR_FILENO); patharg_ = argv[optind]; s = commandvenv("SOX", "sox"); sox_ = s ? strdup(s) : 0;