Update stb (#885)

This commit and, by extension, PR attempts to update `stb` in the most
straightforward way possible as well as include fixes from main repo's
unmerged PRs for cases rearing their ugly heads during everyday usage:

 - 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

Includes additional small fixes that I felt didn't warrant a separate PR.
This commit is contained in:
mataha 2023-12-23 06:39:27 +01:00 committed by GitHub
parent 7faffde303
commit 1bc48bc8e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1560 additions and 1282 deletions

View file

@ -9,8 +9,9 @@ int mulaw(int);
int unmulaw(int); int unmulaw(int);
void *double2byte(long, const void *, double, double) vallocesque; void *double2byte(long, const void *, double, double) vallocesque;
void *byte2double(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 *dct(float[restrict hasatleast 8][8], unsigned,
void *dctjpeg(float[8][8]); float, float, float, float, float);
void *dctjpeg(float[restrict hasatleast 8][8], unsigned);
double det3(const double[3][3]) nosideeffect; double det3(const double[3][3]) nosideeffect;
void *inv3(double[restrict 3][3], const double[restrict 3][3], double); 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]); void *matmul3(double[restrict 3][3], const double[3][3], const double[3][3]);

View file

@ -18,40 +18,40 @@
*/ */
#include "dsp/core/core.h" #include "dsp/core/core.h"
#define DCT(A, B, C, D, E, F, G, H, T, C0, C1, C2, C3, C4) \ #define DCT(A, B, C, D, E, F, G, H, T, C0, C1, C2, C3, C4) \
do { \ do { \
T z1, z2, z3, z4, z5, z11, z13; \ T z1, z2, z3, z4, z5, z11, z13; \
T t0, t1, t2, t3, t4, t5, t6, t7, t10, t11, t12, t13; \ T t0, t1, t2, t3, t4, t5, t6, t7, t10, t11, t12, t13; \
t0 = A + H; \ t0 = A + H; \
t7 = A - H; \ t7 = A - H; \
t1 = B + G; \ t1 = B + G; \
t6 = B - G; \ t6 = B - G; \
t2 = C + F; \ t2 = C + F; \
t5 = C - F; \ t5 = C - F; \
t3 = D + E; \ t3 = D + E; \
t4 = D - E; \ t4 = D - E; \
t10 = t0 + t3; \ t10 = t0 + t3; \
t13 = t0 - t3; \ t13 = t0 - t3; \
t11 = t1 + t2; \ t11 = t1 + t2; \
t12 = t1 - t2; \ t12 = t1 - t2; \
A = t10 + t11; \ A = t10 + t11; \
E = t10 - t11; \ E = t10 - t11; \
z1 = (t12 + t13) * C0; \ z1 = (t12 + t13) * C0; \
C = t13 + z1; \ C = t13 + z1; \
G = t13 - z1; \ G = t13 - z1; \
t10 = t4 + t5; \ t10 = t4 + t5; \
t11 = t5 + t6; \ t11 = t5 + t6; \
t12 = t6 + t7; \ t12 = t6 + t7; \
z5 = (t10 - t12) * C1; \ z5 = (t10 - t12) * C1; \
z2 = t10 * C2 + z5; \ z2 = t10 * C2 + z5; \
z4 = t12 * C3 + z5; \ z4 = t12 * C3 + z5; \
z3 = t11 * C4; \ z3 = t11 * C4; \
z11 = t7 + z3; \ z11 = t7 + z3; \
z13 = t7 - z3; \ z13 = t7 - z3; \
F = z13 + z2; \ F = z13 + z2; \
D = z13 - z2; \ D = z13 - z2; \
B = z11 + z4; \ B = z11 + z4; \
H = z11 - z4; \ H = z11 - z4; \
} while (0) } while (0)
/** /**
@ -65,20 +65,21 @@
* *
* @cost ~100ns * @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; 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], 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); 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], 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); float, c0, c1, c2, c3, c4);
} }
return M; return M;
} }
void *dctjpeg(float M[8][8]) { void *dctjpeg(float M[restrict hasatleast 8][8], unsigned stride) {
return dct(M, .707106781f, .382683433f, .541196100f, 1.306562965f, return dct(M, stride, .707106781f, .382683433f, .541196100f, 1.306562965f,
.707106781f); .707106781f);
} }

View file

@ -5,8 +5,8 @@ LOCAL CHANGES
- Removed undefined behavior - Removed undefined behavior
- Removed BMP [endian code made it 100x slower than PNG/JPEG] - Removed BMP [endian code made it 100x slower than PNG/JPEG]
- Removed PIC [never heard of it] - Removed PIC [never heard of it]
- Removed TGA [consider imaagemagick convert command] - Removed TGA [consider imagemagick convert command]
- Removed PSD [consider imaagemagick convert command] - Removed PSD [consider imagemagick convert command]
- Removed HDR [mine eyes and wikipedia agree stb gamma math is off] - Removed HDR [mine eyes and wikipedia agree stb gamma math is off]
- Patched PNG loading edge case - Patched PNG loading edge case
- Fixed code C standard says is undefined - Fixed code C standard says is undefined
@ -14,10 +14,25 @@ LOCAL CHANGES
- Removed unnecessary ifdefs - Removed unnecessary ifdefs
- Removed MSVC torture code - Removed MSVC torture code
SYNCHRONIZATION POINT SYNCHRONIZATION POINT (`--date=format:"%a %b %d %H:%M:%S %Y %z"`)
commit f67165c2bb2af3060ecae7d20d6f731173485ad0 commit 5736b15f7ea0ffb08dd38af21067c314d6a3aae9
Author: Sean Barrett <sean2@nothings.org> Author: Sean Barrett <seanb@radgametools.com>
Date: Mon Oct 28 09:30:02 2019 -0700 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

View file

@ -1,13 +1,12 @@
/* /* stb_image - v2.29 - public domain image loader - http://nothings.org/stb
* stb_image - v2.23 - public domain image loader - http://nothings.org/stb
* no warranty implied; use at your own risk * no warranty implied; use at your own risk
* *
* [heavily modified by justine tunney] * [heavily modified by justine tunney]
* *
* JPEG baseline & progressive (12 bpc/arithmetic not supported, same * 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) * GIF (*comp always reports as 4-channel)
* HDR (radiance rgbE format)
* PNM (PPM and PGM binary only) * PNM (PPM and PGM binary only)
* *
* Animated GIF still needs a proper API, but here's one way to do it: * Animated GIF still needs a proper API, but here's one way to do it:
@ -18,45 +17,53 @@
* *
* ============================ Contributors ========================= * ============================ Contributors =========================
* *
* Image formats Extensions, features * Image formats Extensions, features
* Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) * Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info)
* Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) * Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info)
* Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) * Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG)
* Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) * Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks)
* Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) * Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG)
* Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) * Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip)
* Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) * Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD)
* github:urraka (animated gif) Junggon Kim (PNM comments) * github:urraka (animated gif) Junggon Kim (PNM comments)
* Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) * Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA)
* socks-the-fox (16-bit PNG) * socks-the-fox (16-bit PNG)
* Jeremy Sawicki (ImageNet JPGs) * Optimizations & bugfixes Jeremy Sawicki (ImageNet JPGs)
* Mikhail Morozov (1-bit BMP) * Fabian "ryg" Giesen Mikhail Morozov (1-bit BMP)
* Optimizations & bugfixes Anael Seghezzi (is-16-bit query) * Arseny Kapoulkine Anael Seghezzi (is-16-bit query)
* Fabian "ryg" Giesen * John-Mark Allen Simon Breuss (16-bit PNM)
* Arseny Kapoulkine
* John-Mark Allen
* Carmelo J Fdez-Aguera * Carmelo J Fdez-Aguera
* *
* Bug & warning fixes * Bug & warning fixes
* Marc LeBlanc David Woo Guillaume George Martins Mozeiko * Marc LeBlanc Laurent Gomila JR Smith
* Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan * Christpher Lloyd Sergio Gonzalez Matvey Cherevko
* Dave Moore Roy Eltham Hayaki Saito Nathan Reed * Phil Jordan Ryamond Barbiero Zack Middleton
* Won Chun Luke Graham Johan Duparc Nick Verigakis * Hayaki Saito Engin Manap
* the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh * Luke Graham Dale Weiler Martins Mozeiko
* Janez Zemva John Bartholomew Michal Cichon github:romigrou * Thomas Ruf Neil Bickford Blazej Dariusz Roszkowski
* Jonathan Blow Ken Hamada Tero Hanninen github:svdijk * Janez Zemva Gregory Mullen Roy Eltham
* Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar * Jonathan Blow Kevin Schmidt
* Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex * Eugene Golushkov Brad Weinberger the Horde3D community
* Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 * Aruelien Pocheville Alexander Veselov github:rlyeh
* Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw * Cass Everitt [reserved] github:romigrou
* Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus * Paul Du Bois github:svdijk
* Julian Raschke Gregory Mullen Baldur Karlsson * Philipp Wiesemann Guillaume George github:snagar
* github:poppolopoppo Christian Floisand Kevin Schmidt JR Smith * Josh Tobin Joseph Thomson github:Zelex
* github:darealshinji Blazej Dariusz Roszkowski github:Michaelangel007 * Julian Raschke Dave Moore github:grim210
*/ * Baldur Karlsson Won Chun github:sammyhw
* Nick Verigakis github:phprus
/* * Luca Sas github:poppolopoppo
* DOCUMENTATION * 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: * Limitations:
* - no 12-bit-per-channel JPEG * - no 12-bit-per-channel JPEG
@ -70,14 +77,15 @@
* // ... x = width, y = height, n = # 8-bit components per pixel ... * // ... x = width, y = height, n = # 8-bit components per pixel ...
* // ... replace '0' with '1'..'4' to force that many 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 * // ... 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: * Standard parameters:
* int *x -- outputs image width in pixels * int *x -- outputs image width in pixels
* int *y -- outputs image height in pixels * int *y -- outputs image height in pixels
* int *channels_in_file -- outputs # of image components in image file * int *channels_in_file -- outputs # of image components in image file
* int desired_channels -- if non-zero, # of image components requested in * 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 * 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 * 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. * 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 * I/O callbacks
@ -163,11 +197,10 @@
* *
* iPhone PNG support: * iPhone PNG support:
* *
* By default we convert iphone-formatted PNGs back to RGB, even though * We optionally support converting iPhone-formatted PNGs (which store
* they are internally encoded differently. You can disable this conversion * premultiplied BGRA) back to RGB, even though they're internally encoded
* by calling stbi_convert_iphone_png_to_rgb(0), in which case * differently. To enable this conversion, call
* you will always just get the native iphone "format" through (which * stbi_convert_iphone_png_to_rgb(1).
* is BGR stored in RGB).
* *
* Call stbi_set_unpremultiply_on_load(1) as well to force a divide per * 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 * 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 * - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still
* want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB * 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 * by Jorge L Rodriguez (@VinoBS) - 2014
* http://github.com/nothings/stb * http://github.com/nothings/stb
* *
@ -214,9 +256,7 @@
* output_pixels, out_w, out_h, 0, * output_pixels, out_w, out_h, 0,
* num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) * num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP)
* // WRAP/REFLECT/ZERO * // WRAP/REFLECT/ZERO
*/ *
/*
* DOCUMENTATION * DOCUMENTATION
* *
* SRGB & FLOATING POINT REPRESENTATION * SRGB & FLOATING POINT REPRESENTATION
@ -348,6 +388,7 @@
* Nathan Reed: warning fixes * Nathan Reed: warning fixes
* *
* REVISIONS * REVISIONS
* 0.97 (2020-02-02) fixed warning
* 0.96 (2019-03-04) fixed warnings * 0.96 (2019-03-04) fixed warnings
* 0.95 (2017-07-23) fixed warnings * 0.95 (2017-07-23) fixed warnings
* 0.94 (2017-03-18) fixed warnings * 0.94 (2017-03-18) fixed warnings

File diff suppressed because it is too large Load diff

View file

@ -13,12 +13,14 @@ enum {
struct FILE; struct FILE;
typedef struct { typedef struct {
int (*read)(void *user, char *data, // fill 'data' with 'size' bytes. return number of bytes actually read
int size); // fill 'data' with 'size' bytes. return number of int (*read)(void *user, char *data, int size);
// bytes actually read
void (*skip)(void *user, int n); // skip the next 'n' bytes, or 'unget' the // skip the next 'n' bytes, or 'unget' the last -n bytes if negative
// last -n bytes if negative void (*skip)(void *user, int n);
int (*eof)(void *user); // returns nonzero if we are at end of file/data
// returns nonzero if we are at end of file/data
int (*eof)(void *user);
} stbi_io_callbacks; } stbi_io_callbacks;
// //
@ -63,7 +65,6 @@ unsigned short *stbi_load_from_file_16(struct FILE *f, int *x, int *y,
int desired_channels); int desired_channels);
// get a VERY brief reason for failure // get a VERY brief reason for failure
// NOT THREADSAFE
const char *stbi_failure_reason(void); const char *stbi_failure_reason(void);
// free the loaded image -- this is just free() // free the loaded image -- this is just free()

View file

@ -1,7 +1,7 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi vi: set et 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 Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the 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]; total_filter += coefficient_group[i];
} }
STBIR_ASSERT(stbir__filter_info_table[filter].kernel( // NOTE(fg): Not actually true in general, nor is there any reason to expect
(float)(in_last_pixel + 1) + 0.5f - in_center_of_out, // it should be. It would be true in exact math but is at best approximately
1 / scale) == 0); // 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 > 0.9);
STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. 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__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio;
} }
STBIR_ASSERT(stbir__filter_info_table[filter].kernel( // NOTE(fg): Not actually true in general, nor is there any reason to expect
(float)(out_last_pixel + 1) + 0.5f - out_center_of_in, // it should be. It would be true in exact math but is at best approximately
scale_ratio) == 0); // 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--) { for (i = out_last_pixel - out_first_pixel; i >= 0; i--) {
if (coefficient_group[i]) break; if (coefficient_group[i]) break;
@ -851,7 +861,7 @@ static float* stbir__get_decode_buffer(stbir__info* stbir_info) {
} }
#define STBIR__DECODE(type, colorspace) \ #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) { static void stbir__decode_scanline(stbir__info* stbir_info, int n) {
int c; int c;
@ -1199,7 +1209,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info,
int out_pixel_index = k * 1; int out_pixel_index = k * 1;
float coefficient = float coefficient =
horizontal_coefficients[coefficient_group + k - n0]; horizontal_coefficients[coefficient_group + k - n0];
STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] += output_buffer[out_pixel_index + 0] +=
decode_buffer[in_pixel_index + 0] * coefficient; 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; int out_pixel_index = k * 2;
float coefficient = float coefficient =
horizontal_coefficients[coefficient_group + k - n0]; horizontal_coefficients[coefficient_group + k - n0];
STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] += output_buffer[out_pixel_index + 0] +=
decode_buffer[in_pixel_index + 0] * coefficient; decode_buffer[in_pixel_index + 0] * coefficient;
output_buffer[out_pixel_index + 1] += 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; int out_pixel_index = k * 3;
float coefficient = float coefficient =
horizontal_coefficients[coefficient_group + k - n0]; horizontal_coefficients[coefficient_group + k - n0];
STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] += output_buffer[out_pixel_index + 0] +=
decode_buffer[in_pixel_index + 0] * coefficient; decode_buffer[in_pixel_index + 0] * coefficient;
output_buffer[out_pixel_index + 1] += 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; int out_pixel_index = k * 4;
float coefficient = float coefficient =
horizontal_coefficients[coefficient_group + k - n0]; horizontal_coefficients[coefficient_group + k - n0];
STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] += output_buffer[out_pixel_index + 0] +=
decode_buffer[in_pixel_index + 0] * coefficient; decode_buffer[in_pixel_index + 0] * coefficient;
output_buffer[out_pixel_index + 1] += output_buffer[out_pixel_index + 1] +=

View file

@ -1,123 +1,21 @@
/* stb_image_write - v1.13 - public domain - http://nothings.org/stb /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
* writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
* no warranty implied; use at your own risk
* Copyright 2023 Justine Alexandra Roberts Tunney
* ABOUT:
* Permission to use, copy, modify, and/or distribute this software for
* This file is a library for writing images to stdio or a callback. any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* The PNG output is not optimal; it is 20-50% larger than the file
* written by a decent optimizing implementation; though providing a THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* custom zlib compress function (see STBIW_ZLIB_COMPRESS) can WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* mitigate that. This library is designed for source code WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* compactness and simplicity, not optimal image file size or AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* run-time performance. DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* USAGE: TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
* 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)
*/
#include "third_party/stb/stb_image_write.h" #include "third_party/stb/stb_image_write.h"
#include "dsp/core/core.h" #include "dsp/core/core.h"
#include "libc/assert.h" #include "libc/assert.h"
@ -131,16 +29,32 @@
#include "libc/str/str.h" #include "libc/str/str.h"
#include "third_party/zlib/zlib.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_UCHAR(x) (unsigned char)((x)&0xff)
#define STBIW_REALLOC_SIZED(p, oldsz, newsz) realloc(p, newsz)
#define stbiw__wpng4(o, a, b, c, d) \
((o)[0] = STBIW_UCHAR(a), (o)[1] = STBIW_UCHAR(b), (o)[2] = STBIW_UCHAR(c), \
(o)[3] = STBIW_UCHAR(d), (o) += 4)
#define stbiw__wp32(data, v) \
stbiw__wpng4(data, (v) >> 24, (v) >> 16, (v) >> 8, (v));
#define stbiw__wptag(data, s) stbiw__wpng4(data, s[0], s[1], s[2], s[3])
typedef struct { typedef struct {
stbi_write_func *func; stbi_write_func *func;
void *context; void *context;
unsigned char buffer[64];
int buf_used;
} stbi__write_context; } stbi__write_context;
int stbi__flip_vertically_on_write = 0; int stbi_write_png_compression_level = 4;
int stbi_write_tga_with_rle = 1; int stbi_write_tga_with_rle = 1;
int stbi_write_force_png_filter = -1;
static int stbi__flip_vertically_on_write = 0;
void stbi_flip_vertically_on_write(int flag) { void stbi_flip_vertically_on_write(int flag) {
stbi__flip_vertically_on_write = flag; stbi__flip_vertically_on_write = flag;
@ -168,9 +82,6 @@ static void stbi__end_write_file(stbi__write_context *s) {
fclose((FILE *)s->context); fclose((FILE *)s->context);
} }
typedef unsigned int stbiw_uint32;
typedef int stb_image_write_test[sizeof(stbiw_uint32) == 4 ? 1 : -1];
static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) {
while (*fmt) { while (*fmt) {
switch (*fmt++) { switch (*fmt++) {
@ -190,7 +101,7 @@ static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) {
break; break;
} }
case '4': { case '4': {
stbiw_uint32 x = va_arg(v, int); unsigned int x = va_arg(v, int);
unsigned char b[4]; unsigned char b[4];
b[0] = STBIW_UCHAR(x); b[0] = STBIW_UCHAR(x);
b[1] = STBIW_UCHAR(x >> 8); b[1] = STBIW_UCHAR(x >> 8);
@ -212,17 +123,31 @@ static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) {
va_end(v); 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) { static void stbiw__putc(stbi__write_context *s, unsigned char c) {
s->func(s->context, &c, 1); 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, static void stbiw__write3(stbi__write_context *s, unsigned char a,
unsigned char b, unsigned char c) { unsigned char b, unsigned char c) {
unsigned char arr[3]; int n;
arr[0] = a; if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) stbiw__write_flush(s);
arr[1] = b; n = s->buf_used;
arr[2] = c; s->buf_used = n + 3;
s->func(s->context, arr, 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, static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp,
@ -231,7 +156,7 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp,
unsigned char bg[3] = {255, 0, 255}, px[3]; unsigned char bg[3] = {255, 0, 255}, px[3];
int k; 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) { switch (comp) {
case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as
@ -240,7 +165,7 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp,
if (expand_mono) if (expand_mono)
stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp
else else
s->func(s->context, d, 1); // monochrome TGA stbiw__write1(s, d[0]); // monochrome TGA
break; break;
case 4: case 4:
if (!write_alpha) { if (!write_alpha) {
@ -254,14 +179,14 @@ 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]); stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);
break; 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, static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir,
int x, int y, int comp, void *data, int x, int y, int comp, void *data,
int write_alpha, int scanline_pad, int write_alpha, int scanline_pad,
int expand_mono) { int expand_mono) {
stbiw_uint32 zero = 0; unsigned int zero = 0;
int i, j, j_end; int i, j, j_end;
if (y <= 0) return; if (y <= 0) return;
if (stbi__flip_vertically_on_write) vdir *= -1; if (stbi__flip_vertically_on_write) vdir *= -1;
@ -277,6 +202,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; unsigned char *d = (unsigned char *)data + (j * x + i) * comp;
stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);
} }
stbiw__write_flush(s);
s->func(s->context, &zero, scanline_pad); s->func(s->context, &zero, scanline_pad);
} }
} }
@ -299,25 +225,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, static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp,
const void *data) { const void *data) {
int pad = (-x * 3) & 3; if (comp != 4) {
return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *)data, 0, pad, // write RGB bitmap
"11 4 22 4" int pad;
"4 44 22 444444", pad = (-x * 3) & 3;
'B', 'M', 14 + 40 + (x * 3 + pad) * y, 0, 0, return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *)data, 0, pad,
14 + 40, // file header "11 4 22 4"
40, x, y, 1, 24, 0, 0, 0, 0, 0, 0); // bitmap header "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 stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y,
int comp, const void *data) { int comp, const void *data) {
stbi__write_context s; stbi__write_context s = {0};
stbi__start_write_callbacks(&s, func, context); stbi__start_write_callbacks(&s, func, context);
return stbi_write_bmp_core(&s, x, y, comp, data); return stbi_write_bmp_core(&s, x, y, comp, data);
} }
int stbi_write_bmp(char const *filename, int x, int y, int comp, int stbi_write_bmp(char const *filename, int x, int y, int comp,
const void *data) { const void *data) {
stbi__write_context s; stbi__write_context s = {0};
if (stbi__start_write_file(&s, filename)) { if (stbi__start_write_file(&s, filename)) {
int r = stbi_write_bmp_core(&s, x, y, comp, data); int r = stbi_write_bmp_core(&s, x, y, comp, data);
stbi__end_write_file(&s); stbi__end_write_file(&s);
@ -393,31 +335,32 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp,
if (diff) { if (diff) {
unsigned char header = STBIW_UCHAR(len - 1); unsigned char header = STBIW_UCHAR(len - 1);
s->func(s->context, &header, 1); stbiw__write1(s, header);
for (k = 0; k < len; ++k) { for (k = 0; k < len; ++k) {
stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);
} }
} else { } else {
unsigned char header = STBIW_UCHAR(len - 129); 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_pixel(s, -1, comp, has_alpha, 0, begin);
} }
} }
} }
stbiw__write_flush(s);
} }
return 1; return 1;
} }
int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y,
int comp, const void *data) { int comp, const void *data) {
stbi__write_context s; stbi__write_context s = {0};
stbi__start_write_callbacks(&s, func, context); stbi__start_write_callbacks(&s, func, context);
return stbi_write_tga_core(&s, x, y, comp, (void *)data); return stbi_write_tga_core(&s, x, y, comp, (void *)data);
} }
int stbi_write_tga(char const *filename, int x, int y, int comp, int stbi_write_tga(char const *filename, int x, int y, int comp,
const void *data) { const void *data) {
stbi__write_context s; stbi__write_context s = {0};
if (stbi__start_write_file(&s, filename)) { if (stbi__start_write_file(&s, filename)) {
int r = stbi_write_tga_core(&s, x, y, comp, (void *)data); int r = stbi_write_tga_core(&s, x, y, comp, (void *)data);
stbi__end_write_file(&s); stbi__end_write_file(&s);
@ -426,6 +369,250 @@ int stbi_write_tga(char const *filename, int x, int y, int comp,
return 0; return 0;
} }
/*
* PNG writer
*/
static unsigned char *stbi_zlib_compress(unsigned char *data, int size,
int *out_len, int quality) {
unsigned long newsize;
unsigned char *newdata, *trimdata;
assert(0 <= size && size <= INT_MAX);
if ((newdata = malloc((newsize = compressBound(size)))) &&
compress2(newdata, &newsize, data, size,
stbi_write_png_compression_level) == Z_OK) {
*out_len = newsize;
if ((trimdata = realloc(newdata, newsize))) {
return trimdata;
} else {
return newdata;
}
}
free(newdata);
return NULL;
}
static void stbiw__wpcrc(unsigned char **data, int len) {
unsigned int crc = crc32(0, *data - len - 4, len + 4);
stbiw__wp32(*data, crc);
}
forceinline unsigned char stbiw__paeth(int a, int b, int c) {
int p = a + b - c, pa = abs(p - a), pb = abs(p - b), pc = abs(p - c);
if (pa <= pb && pa <= pc) return STBIW_UCHAR(a);
if (pb <= pc) return STBIW_UCHAR(b);
return STBIW_UCHAR(c);
}
// @OPTIMIZE: provide an option that always forces left-predict or paeth predict
static void stbiw__encode_png_line(const unsigned char *pixels,
int stride_bytes, int width, int height,
int y, int n, int filter_type,
signed char *line_buffer) {
int mapping[] = {0, 1, 2, 3, 4};
int firstmap[] = {0, 1, 0, 5, 6};
const unsigned char *z;
int *mymap, i, type, signed_stride;
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;
if (type == 0) {
memcpy(line_buffer, z, width * n);
return;
}
for (i = 0; i < n; ++i) {
switch (type) {
case 1:
line_buffer[i] = z[i];
break;
case 2:
line_buffer[i] = z[i] - z[i - signed_stride];
break;
case 3:
line_buffer[i] = z[i] - (z[i - signed_stride] >> 1);
break;
case 4:
line_buffer[i] =
(signed char)(z[i] - stbiw__paeth(0, z[i - signed_stride], 0));
break;
case 5:
line_buffer[i] = z[i];
break;
case 6:
line_buffer[i] = z[i];
break;
}
}
switch (type) {
case 1:
for (i = n; i < width * n; ++i) {
line_buffer[i] = z[i] - z[i - n];
}
break;
case 2:
for (i = n; i < width * n; ++i) {
line_buffer[i] = z[i] - z[i - signed_stride];
}
break;
case 3:
for (i = n; i < width * n; ++i) {
line_buffer[i] = z[i] - ((z[i - n] + z[i - signed_stride]) >> 1);
}
break;
case 4:
for (i = n; i < width * n; ++i) {
line_buffer[i] = z[i] - stbiw__paeth(z[i - n], z[i - signed_stride],
z[i - signed_stride - n]);
}
break;
case 5:
for (i = n; i < width * n; ++i) {
line_buffer[i] = z[i] - (z[i - n] >> 1);
}
break;
case 6:
for (i = n; i < width * n; ++i) {
line_buffer[i] = z[i] - stbiw__paeth(z[i - n], 0, 0);
}
break;
}
}
unsigned char *stbi_write_png_to_mem(const unsigned char *pixels,
int stride_bytes, int x, int y, int n,
int *out_len) {
int force_filter = stbi_write_force_png_filter;
int ctype[5] = {-1, 0, 4, 2, 6};
unsigned char sig[8] = {137, 80, 78, 71, 13, 10, 26, 10};
unsigned char *out, *o, *filt, *zlib;
signed char *line_buffer;
int j, zlen;
if (stride_bytes == 0) stride_bytes = x * n;
if (force_filter >= 5) {
force_filter = -1;
}
filt = malloc((x * n + 1) * y);
if (!filt) return 0;
line_buffer = malloc(x * n);
if (!line_buffer) {
free(filt);
return 0;
}
for (j = 0; j < y; ++j) {
int filter_type;
if (force_filter > -1) {
filter_type = force_filter;
stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, force_filter,
line_buffer);
} else { // Estimate the best filter by running through all of them:
int best_filter = 0, best_filter_val = 0x7fffffff, est, i;
for (filter_type = 0; filter_type < 5; filter_type++) {
stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, filter_type,
line_buffer);
// Estimate the entropy of the line using this filter; the less, the
// better.
est = 0;
for (i = 0; i < x * n; ++i) {
est += abs((signed char)line_buffer[i]);
}
if (est < best_filter_val) {
best_filter_val = est;
best_filter = filter_type;
}
}
if (filter_type != best_filter) { // If the last iteration already got us
// the best filter, don't redo it
stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, best_filter,
line_buffer);
filter_type = best_filter;
}
}
// when we get here, filter_type contains the filter type, and line_buffer
// contains the data
filt[j * (x * n + 1)] = (unsigned char)filter_type;
memmove(filt + j * (x * n + 1) + 1, line_buffer, x * n);
}
free(line_buffer);
zlib = stbi_zlib_compress(filt, y * (x * n + 1), &zlen,
stbi_write_png_compression_level);
free(filt);
if (!zlib) return 0;
// each tag requires 12 bytes of overhead
out = malloc(8 + 12 + 13 + 12 + zlen + 12);
if (!out) return 0;
*out_len = 8 + 12 + 13 + 12 + zlen + 12;
o = out;
memmove(o, sig, 8);
o += 8;
stbiw__wp32(o, 13); // header length
stbiw__wptag(o, "IHDR");
stbiw__wp32(o, x);
stbiw__wp32(o, y);
*o++ = 8;
*o++ = STBIW_UCHAR(ctype[n]);
*o++ = 0;
*o++ = 0;
*o++ = 0;
stbiw__wpcrc(&o, 13);
stbiw__wp32(o, zlen);
stbiw__wptag(o, "IDAT");
memmove(o, zlib, zlen);
o += zlen;
free(zlib);
stbiw__wpcrc(&o, zlen);
stbiw__wp32(o, 0);
stbiw__wptag(o, "IEND");
stbiw__wpcrc(&o, 0);
assert(o == out + *out_len);
return out;
}
int stbi_write_png(const char *filename, int x, int y, int comp,
const void *data, int stride_bytes) {
int len;
FILE *f;
unsigned char *png;
png = stbi_write_png_to_mem(data, stride_bytes, x, y, comp, &len);
if (png == NULL) return 0;
f = fopen(filename, "wb");
if (!f) {
free(png);
return 0;
}
fwrite(png, 1, len, f);
fclose(f);
free(png);
return 1;
}
int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y,
int comp, const void *data, int stride_bytes) {
int len;
unsigned char *png;
png = stbi_write_png_to_mem((const unsigned char *)data, stride_bytes, x, y,
comp, &len);
if (png == NULL) return 0;
func(context, png, len);
free(png);
return 1;
}
/* JPEG writer /* JPEG writer
* *
* This is based on Jon Olick's jo_jpeg.cpp: * This is based on Jon Olick's jo_jpeg.cpp:
@ -472,24 +659,25 @@ static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) {
} }
static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf, 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 HTDC[256][2],
const unsigned short HTAC[256][2]) { const unsigned short HTAC[256][2]) {
const unsigned short EOB[2] = {HTAC[0x00][0], HTAC[0x00][1]}; const unsigned short EOB[2] = {HTAC[0x00][0], HTAC[0x00][1]};
const unsigned short M16zeroes[2] = {HTAC[0xF0][0], HTAC[0xF0][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]; int DU[64];
dctjpeg((void *)CDU); dctjpeg((void *)CDU, du_stride / 8);
// Quantize/descale/zigzag the coefficients // Quantize/descale/zigzag the coefficients
for (i = 0; i < 64; ++i) { for (j = 0, y = 0; y < 8; ++y) {
float v = CDU[i] * fdtbl[i]; for (x = 0; x < 8; ++x, ++j) {
DU[stbiw__jpg_ZigZag[i]] = v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f); float v;
// DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + i = y * du_stride + x;
// 0.5f)); ceilf() and floorf() are C99, not C89, but I /think/ they're not v = CDU[i] * fdtbl[j];
// needed here anyway? DU[stbiw__jpg_ZigZag[j]] = v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f);
/* DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? v - 0.5f : v + 0.5f); */ }
} }
// Encode DC // Encode DC
@ -709,7 +897,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height,
1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 1.0f * 2.828427125f, 0.785694958f * 2.828427125f,
0.541196100f * 2.828427125f, 0.275899379f * 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]; float fdtbl_Y[64], fdtbl_UV[64];
unsigned char YTable[64], UVTable[64]; unsigned char YTable[64], UVTable[64];
@ -718,6 +906,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height,
} }
quality = quality ? quality : 97; quality = quality ? quality : 97;
subsample = quality <= 97 ? 1 : 0;
quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; quality = quality < 1 ? 1 : quality > 100 ? 100 : quality;
quality = quality < 50 ? 5000 / quality : 200 - quality * 2; quality = quality < 50 ? 5000 / quality : 200 - quality * 2;
@ -758,7 +947,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height,
STBIW_UCHAR(width), STBIW_UCHAR(width),
3, 3,
1, 1,
0x11, (unsigned char)(subsample ? 0x22 : 0x11),
0, 0,
2, 2,
0x11, 0x11,
@ -802,42 +991,92 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height,
// Encode 8x8 macroblocks // Encode 8x8 macroblocks
{ {
static const unsigned short fillBits[] = {0x7F, 7}; static const unsigned short fillBits[] = {0x7F, 7};
const unsigned char *imageData = (const unsigned char *)data;
int DCY = 0, DCU = 0, DCV = 0; int DCY = 0, DCU = 0, DCV = 0;
int bitBuf = 0, bitCnt = 0; int bitBuf = 0, bitCnt = 0;
// comp == 2 is grey+alpha (alpha is ignored) // comp == 2 is grey+alpha (alpha is ignored)
int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; 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; int x, y, pos;
for (y = 0; y < height; y += 8) { if (subsample) {
for (x = 0; x < width; x += 8) { for (y = 0; y < height; y += 16) {
float YDU[64], UDU[64], VDU[64]; for (x = 0; x < width; x += 16) {
for (row = y, pos = 0; row < y + 8; ++row) { float Y[256], U[256], V[256];
// row >= height => use last input row for (row = y, pos = 0; row < y + 16; ++row) {
int clamped_row = (row < height) ? row : height - 1; // row >= height => use last input row
int base_p = int clamped_row = (row < height) ? row : height - 1;
(stbi__flip_vertically_on_write ? (height - 1 - clamped_row) int base_p =
: clamped_row) * (stbi__flip_vertically_on_write ? (height - 1 - clamped_row)
width * comp; : clamped_row) *
for (col = x; col < x + 8; ++col, ++pos) { width * comp;
float r, g, b; for (col = x; col < x + 16; ++col, ++pos) {
// if col >= width => use pixel from last input column // if col >= width => use pixel from last input column
int p = base_p + ((col < width) ? col : (width - 1)) * comp; 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;
}
}
r = imageData[p + 0]; DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 0, 16, fdtbl_Y,
g = imageData[p + ofsG]; DCY, YDC_HT, YAC_HT);
b = imageData[p + ofsB]; DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 8, 16, fdtbl_Y,
YDU[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128; DCY, YDC_HT, YAC_HT);
UDU[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b; DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 128, 16, fdtbl_Y,
VDU[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b; 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, DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY,
YDC_HT, YAC_HT); YDC_HT, YAC_HT);
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU,
UVDC_HT, UVAC_HT); UVDC_HT, UVAC_HT);
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV,
UVDC_HT, UVAC_HT); UVDC_HT, UVAC_HT);
}
} }
} }
@ -854,14 +1093,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 stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y,
int comp, const void *data, int quality) { int comp, const void *data, int quality) {
stbi__write_context s; stbi__write_context s = {0};
stbi__start_write_callbacks(&s, func, context); stbi__start_write_callbacks(&s, func, context);
return stbi_write_jpg_core(&s, x, y, comp, (void *)data, quality); 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, int stbi_write_jpg(char const *filename, int x, int y, int comp,
const void *data, int quality) { const void *data, int quality) {
stbi__write_context s; stbi__write_context s = {0};
if (stbi__start_write_file(&s, filename)) { if (stbi__start_write_file(&s, filename)) {
int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); int r = stbi_write_jpg_core(&s, x, y, comp, data, quality);
stbi__end_write_file(&s); stbi__end_write_file(&s);
@ -1026,14 +1265,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 stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y,
int comp, const float *data) { int comp, const float *data) {
stbi__write_context s; stbi__write_context s = {0};
stbi__start_write_callbacks(&s, func, context); stbi__start_write_callbacks(&s, func, context);
return stbi_write_hdr_core(&s, x, y, comp, (float *)data); return stbi_write_hdr_core(&s, x, y, comp, (float *)data);
} }
int stbi_write_hdr(char const *filename, int x, int y, int comp, int stbi_write_hdr(char const *filename, int x, int y, int comp,
const float *data) { const float *data) {
stbi__write_context s; stbi__write_context s = {0};
if (stbi__start_write_file(&s, filename)) { if (stbi__start_write_file(&s, filename)) {
int r = stbi_write_hdr_core(&s, x, y, comp, (float *)data); int r = stbi_write_hdr_core(&s, x, y, comp, (float *)data);
stbi__end_write_file(&s); stbi__end_write_file(&s);

View file

@ -3,7 +3,6 @@
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
extern int stbi_write_png_compression_level; 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_tga_with_rle;
extern int stbi_write_force_png_filter; extern int stbi_write_force_png_filter;

View file

@ -1,379 +0,0 @@
/* 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)
*/
#include "libc/assert.h"
#include "libc/fmt/conv.h"
#include "libc/limits.h"
#include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "third_party/stb/stb_image_write.h"
#include "third_party/zlib/zlib.h"
#define STBIW_UCHAR(x) (unsigned char)((x)&0xff)
#define stbiw__wpng4(o, a, b, c, d) \
((o)[0] = STBIW_UCHAR(a), (o)[1] = STBIW_UCHAR(b), (o)[2] = STBIW_UCHAR(c), \
(o)[3] = STBIW_UCHAR(d), (o) += 4)
#define stbiw__wp32(data, v) \
stbiw__wpng4(data, (v) >> 24, (v) >> 16, (v) >> 8, (v));
#define stbiw__wptag(data, s) stbiw__wpng4(data, s[0], s[1], s[2], s[3])
int stbi_write_png_compression_level = 4;
int stbi_write_force_png_filter = -1;
static unsigned char *stbi_zlib_compress(unsigned char *data, int size,
int *out_len, int quality) {
unsigned long newsize;
unsigned char *newdata, *trimdata;
assert(0 <= size && size <= INT_MAX);
if ((newdata = malloc((newsize = compressBound(size)))) &&
compress2(newdata, &newsize, data, size,
stbi_write_png_compression_level) == Z_OK) {
*out_len = newsize;
if ((trimdata = realloc(newdata, newsize))) {
return trimdata;
} else {
return newdata;
}
}
free(newdata);
return NULL;
}
static void stbiw__wpcrc(unsigned char **data, int len) {
unsigned int crc = crc32(0, *data - len - 4, len + 4);
stbiw__wp32(*data, crc);
}
forceinline unsigned char stbiw__paeth(int a, int b, int c) {
int p = a + b - c, pa = abs(p - a), pb = abs(p - b), pc = abs(p - c);
if (pa <= pb && pa <= pc) return STBIW_UCHAR(a);
if (pb <= pc) return STBIW_UCHAR(b);
return STBIW_UCHAR(c);
}
// @OPTIMIZE: provide an option that always forces left-predict or paeth predict
static void stbiw__encode_png_line(const unsigned char *pixels,
int stride_bytes, int width, int height,
int y, int n, int filter_type,
signed char *line_buffer) {
int mapping[] = {0, 1, 2, 3, 4};
int firstmap[] = {0, 1, 0, 5, 6};
const unsigned char *z;
int *mymap, i, type, signed_stride;
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;
if (type == 0) {
memcpy(line_buffer, z, width * n);
return;
}
for (i = 0; i < n; ++i) {
switch (type) {
case 1:
line_buffer[i] = z[i];
break;
case 2:
line_buffer[i] = z[i] - z[i - signed_stride];
break;
case 3:
line_buffer[i] = z[i] - (z[i - signed_stride] >> 1);
break;
case 4:
line_buffer[i] =
(signed char)(z[i] - stbiw__paeth(0, z[i - signed_stride], 0));
break;
case 5:
line_buffer[i] = z[i];
break;
case 6:
line_buffer[i] = z[i];
break;
}
}
switch (type) {
case 1:
for (i = n; i < width * n; ++i) {
line_buffer[i] = z[i] - z[i - n];
}
break;
case 2:
for (i = n; i < width * n; ++i) {
line_buffer[i] = z[i] - z[i - signed_stride];
}
break;
case 3:
for (i = n; i < width * n; ++i) {
line_buffer[i] = z[i] - ((z[i - n] + z[i - signed_stride]) >> 1);
}
break;
case 4:
for (i = n; i < width * n; ++i) {
line_buffer[i] = z[i] - stbiw__paeth(z[i - n], z[i - signed_stride],
z[i - signed_stride - n]);
}
break;
case 5:
for (i = n; i < width * n; ++i) {
line_buffer[i] = z[i] - (z[i - n] >> 1);
}
break;
case 6:
for (i = n; i < width * n; ++i) {
line_buffer[i] = z[i] - stbiw__paeth(z[i - n], 0, 0);
}
break;
}
}
unsigned char *stbi_write_png_to_mem(const unsigned char *pixels,
int stride_bytes, int x, int y, int n,
int *out_len) {
int force_filter = stbi_write_force_png_filter;
int ctype[5] = {-1, 0, 4, 2, 6};
unsigned char sig[8] = {137, 80, 78, 71, 13, 10, 26, 10};
unsigned char *out, *o, *filt, *zlib;
signed char *line_buffer;
int j, zlen;
if (stride_bytes == 0) stride_bytes = x * n;
if (force_filter >= 5) {
force_filter = -1;
}
filt = malloc((x * n + 1) * y);
if (!filt) return 0;
line_buffer = malloc(x * n);
if (!line_buffer) {
free(filt);
return 0;
}
for (j = 0; j < y; ++j) {
int filter_type;
if (force_filter > -1) {
filter_type = force_filter;
stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, force_filter,
line_buffer);
} else { // Estimate the best filter by running through all of them:
int best_filter = 0, best_filter_val = 0x7fffffff, est, i;
for (filter_type = 0; filter_type < 5; filter_type++) {
stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, filter_type,
line_buffer);
// Estimate the entropy of the line using this filter; the less, the
// better.
est = 0;
for (i = 0; i < x * n; ++i) {
est += abs((signed char)line_buffer[i]);
}
if (est < best_filter_val) {
best_filter_val = est;
best_filter = filter_type;
}
}
if (filter_type != best_filter) { // If the last iteration already got us
// the best filter, don't redo it
stbiw__encode_png_line(pixels, stride_bytes, x, y, j, n, best_filter,
line_buffer);
filter_type = best_filter;
}
}
// when we get here, filter_type contains the filter type, and line_buffer
// contains the data
filt[j * (x * n + 1)] = (unsigned char)filter_type;
memmove(filt + j * (x * n + 1) + 1, line_buffer, x * n);
}
free(line_buffer);
zlib = stbi_zlib_compress(filt, y * (x * n + 1), &zlen,
stbi_write_png_compression_level);
free(filt);
if (!zlib) return 0;
// each tag requires 12 bytes of overhead
out = malloc(8 + 12 + 13 + 12 + zlen + 12);
if (!out) return 0;
*out_len = 8 + 12 + 13 + 12 + zlen + 12;
o = out;
memmove(o, sig, 8);
o += 8;
stbiw__wp32(o, 13); // header length
stbiw__wptag(o, "IHDR");
stbiw__wp32(o, x);
stbiw__wp32(o, y);
*o++ = 8;
*o++ = STBIW_UCHAR(ctype[n]);
*o++ = 0;
*o++ = 0;
*o++ = 0;
stbiw__wpcrc(&o, 13);
stbiw__wp32(o, zlen);
stbiw__wptag(o, "IDAT");
memmove(o, zlib, zlen);
o += zlen;
free(zlib);
stbiw__wpcrc(&o, zlen);
stbiw__wp32(o, 0);
stbiw__wptag(o, "IEND");
stbiw__wpcrc(&o, 0);
assert(o == out + *out_len);
return out;
}
int stbi_write_png(const char *filename, int x, int y, int comp,
const void *data, int stride_bytes) {
int len;
FILE *f;
unsigned char *png;
png = stbi_write_png_to_mem(data, stride_bytes, x, y, comp, &len);
if (png == NULL) return 0;
f = fopen(filename, "wb");
if (!f) {
free(png);
return 0;
}
fwrite(png, 1, len, f);
fclose(f);
free(png);
return 1;
}
int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y,
int comp, const void *data, int stride_bytes) {
int len;
unsigned char *png;
png = stbi_write_png_to_mem((const unsigned char *)data, stride_bytes, x, y,
comp, &len);
if (png == NULL) return 0;
func(context, png, len);
free(png);
return 1;
}

View file

@ -1,29 +1,20 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:3;tab-width:8;coding:utf-8 -*-│ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=3 sts=3 sw=3 fenc=utf-8 :vi vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2023 Justine Alexandra Roberts Tunney
stb_truetype Permission to use, copy, modify, and/or distribute this software for
Copyright 2017 Sean Barrett any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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_rect_pack.h" #include "third_party/stb/stb_rect_pack.h"
#include "libc/assert.h" #include "libc/assert.h"
@ -41,8 +32,6 @@ asm(".include \"libc/disclaimer.inc\"");
// Useful for e.g. packing rectangular textures into an atlas. // Useful for e.g. packing rectangular textures into an atlas.
// Does not do rotation. // Does not do rotation.
// //
// in the file that you want to have the implementation.
//
// Not necessarily the awesomest packing method, but better than // Not necessarily the awesomest packing method, but better than
// the totally naive one in stb_truetype (which is primarily what // the totally naive one in stb_truetype (which is primarily what
// this is meant to replace). // this is meant to replace).
@ -390,7 +379,11 @@ static int rect_height_compare(const void *a, const void *b)
return -1; return -1;
if (p->h < q->h) if (p->h < q->h)
return 1; 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) static int rect_original_order(const void *a, const void *b)

View file

@ -1,5 +1,5 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:3;tab-width:8;coding:utf-8 -*-│ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=3 sts=3 sw=3 fenc=utf-8 :vi vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
stb_truetype stb_truetype

View file

@ -32,6 +32,7 @@
// manxorist@github saga musix github:infatum // manxorist@github saga musix github:infatum
// Timur Gagiev Maxwell Koo // Timur Gagiev Maxwell Koo
// //
#include "third_party/stb/stb_vorbis.h" #include "third_party/stb/stb_vorbis.h"
#include "libc/assert.h" #include "libc/assert.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
@ -45,6 +46,11 @@
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/str/str.h" #include "libc/str/str.h"
asm(".ident\t\"\\n\\n\
stb_vorbis (Public Domain)\\n\
Credit: Sean Barrett, et al.\\n\
http://nothings.org/stb\"");
// STB_VORBIS_NO_PUSHDATA_API // STB_VORBIS_NO_PUSHDATA_API
// does not compile the code for the various stb_vorbis_*_pushdata() // does not compile the code for the various stb_vorbis_*_pushdata()
// functions // functions
@ -343,6 +349,10 @@ struct stb_vorbis {
unsigned int temp_memory_required; unsigned int temp_memory_required;
unsigned int setup_temp_memory_required; unsigned int setup_temp_memory_required;
char *vendor;
int comment_list_length;
char **comment_list;
// input config // input config
#ifndef STB_VORBIS_NO_STDIO #ifndef STB_VORBIS_NO_STDIO
FILE *f; FILE *f;
@ -358,8 +368,11 @@ struct stb_vorbis {
uint8 push_mode; uint8 push_mode;
// the page to seek to when seeking to start, may be zero
uint32 first_audio_page_offset; 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; ProbedPage p_first, p_last;
// memory management // memory management
@ -493,7 +506,7 @@ static dontinline void *make_block_array(void *mem, int count, int size) {
} }
static dontinline void *setup_malloc(vorb *f, int sz) { 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; f->setup_memory_required += sz;
if (f->alloc.alloc_buffer) { if (f->alloc.alloc_buffer) {
void *p = (char *)f->alloc.alloc_buffer + f->setup_offset; void *p = (char *)f->alloc.alloc_buffer + f->setup_offset;
@ -510,7 +523,7 @@ static dontinline void setup_free(vorb *f, void *p) {
} }
static dontinline void *setup_temp_malloc(vorb *f, int sz) { 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->alloc.alloc_buffer) {
if (f->temp_offset - sz < f->setup_offset) return NULL; if (f->temp_offset - sz < f->setup_offset) return NULL;
f->temp_offset -= sz; f->temp_offset -= sz;
@ -521,7 +534,7 @@ static dontinline void *setup_temp_malloc(vorb *f, int sz) {
static dontinline void setup_temp_free(vorb *f, void *p, int sz) { static dontinline void setup_temp_free(vorb *f, void *p, int sz) {
if (f->alloc.alloc_buffer) { if (f->alloc.alloc_buffer) {
f->temp_offset += (sz + 3) & ~3; f->temp_offset += (sz + 7) & ~7;
return; return;
} }
free(p); free(p);
@ -593,7 +606,7 @@ static float float32_unpack(uint32 x) {
uint32 sign = x & 0x80000000; uint32 sign = x & 0x80000000;
uint32 exp = (x & 0x7fe00000) >> 21; uint32 exp = (x & 0x7fe00000) >> 21;
double res = sign ? -(double)mantissa : (double)mantissa; 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 // zlib & jpeg huffman tables assume that the output symbols
@ -636,6 +649,8 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) {
assert(c->sorted_entries == 0); assert(c->sorted_entries == 0);
return TRUE; return TRUE;
} }
// no error return required, code reading lens checks this
assert(len[k] < 32);
// add to the list // add to the list
add_entry(c, 0, k, m++, len[k], values); add_entry(c, 0, k, m++, len[k], values);
// add all available leaves // add all available leaves
@ -648,6 +663,8 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) {
uint32 res; uint32 res;
int z = len[i], y; int z = len[i], y;
if (z == NO_CODE) continue; 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, // find lowest available leaf (should always be earliest,
// which is what the specification calls for) // which is what the specification calls for)
// note that this property, and the fact we can never have // note that this property, and the fact we can never have
@ -659,12 +676,10 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) {
return FALSE; return FALSE;
} }
res = available[z]; res = available[z];
assert(z >= 0 && z < 32);
available[z] = 0; available[z] = 0;
add_entry(c, ReverseBits32(res), i, m++, len[i], values); add_entry(c, ReverseBits32(res), i, m++, len[i], values);
// propagate availability up the tree // propagate availability up the tree
if (z != len[i]) { if (z != len[i]) {
assert(len[i] >= 0 && len[i] < 32);
for (y = len[i]; y > z; --y) { for (y = len[i]; y > z; --y) {
assert(available[y] == 0); assert(available[y] == 0);
available[y] = res + (1 << (32 - y)); available[y] = res + (1 << (32 - y));
@ -991,6 +1006,9 @@ static int capture_pattern(vorb *f) {
static int start_page_no_capturepattern(vorb *f) { static int start_page_no_capturepattern(vorb *f) {
uint32 loc0, loc1, n; 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 // stream structure version
if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version);
// header flag // header flag
@ -1027,14 +1045,12 @@ static int start_page_no_capturepattern(vorb *f) {
} }
if (f->first_decode) { if (f->first_decode) {
int i, len; int i, len;
ProbedPage p;
len = 0; len = 0;
for (i = 0; i < f->segment_count; ++i) len += f->segments[i]; for (i = 0; i < f->segment_count; ++i) len += f->segments[i];
len += 27 + f->segment_count; len += 27 + f->segment_count;
p.page_start = f->first_audio_page_offset;
p.page_end = p.page_start + len; f->p_first.page_end = f->p_first.page_start + len;
p.last_decoded_sample = loc0; f->p_first.last_decoded_sample = loc0;
f->p_first = p;
} }
f->next_seg = 0; f->next_seg = 0;
return TRUE; return TRUE;
@ -1124,6 +1140,15 @@ static int get8_packet(vorb *f) {
return x; 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) { static void flush_packet(vorb *f) {
while (get8_packet_raw(f) != EOP) while (get8_packet_raw(f) != EOP)
; ;
@ -1153,7 +1178,7 @@ static uint32 get_bits(vorb *f, int n) {
f->valid_bits += 8; f->valid_bits += 8;
} }
} }
if (f->valid_bits < 0) return 0; assert(f->valid_bits >= n);
z = f->acc & ((1 << n) - 1); z = f->acc & ((1 << n) - 1);
f->acc >>= n; f->acc >>= n;
f->valid_bits -= n; f->valid_bits -= n;
@ -1225,7 +1250,7 @@ static int codebook_decode_scalar_raw(vorb *f, Codebook *c) {
assert(!c->sparse); assert(!c->sparse);
for (i = 0; i < c->entries; ++i) { for (i = 0; i < c->entries; ++i) {
if (c->codeword_lengths[i] == NO_CODE) continue; 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]) { if (f->valid_bits >= c->codeword_lengths[i]) {
f->acc >>= c->codeword_lengths[i]; f->acc >>= c->codeword_lengths[i];
f->valid_bits -= c->codeword_lengths[i]; f->valid_bits -= c->codeword_lengths[i];
@ -1414,7 +1439,8 @@ static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c,
// buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter),
// and the length we'll be using (effective) // and the length we'll be using (effective)
if (c_inter + p_inter * ch + effective > len * ch) { 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 #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
@ -1717,49 +1743,7 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n,
++class_set; ++class_set;
#endif #endif
} }
} else if (ch == 1) { } else if (ch > 2) {
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 {
while (pcount < part_read) { while (pcount < part_read) {
int z = r->begin + pcount * r->part_size; int z = r->begin + pcount * r->part_size;
int c_inter = z % ch, p_inter = z / ch; int c_inter = z % ch, p_inter = z / ch;
@ -2165,34 +2149,33 @@ static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A,
while (z > base) { while (z > base) {
float k00, k11; float k00, k11;
float l00, l11;
k00 = z[-0] - z[-8]; k00 = z[-0] - z[-8];
k11 = z[-1] - z[-9]; k11 = z[-1] - z[-9];
l00 = z[-2] - z[-10];
l11 = z[-3] - z[-11];
z[-0] = z[-0] + z[-8]; z[-0] = z[-0] + z[-8];
z[-1] = z[-1] + z[-9]; 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[-2] = z[-2] + z[-10];
z[-3] = z[-3] + z[-11]; z[-3] = z[-3] + z[-11];
z[-10] = (k00 + k11) * A2; z[-8] = k00;
z[-11] = (k11 - k00) * A2; 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]; k11 = z[-5] - z[-13];
l00 = z[-6] - z[-14];
l11 = z[-7] - z[-15];
z[-4] = z[-4] + z[-12]; z[-4] = z[-4] + z[-12];
z[-5] = z[-5] + z[-13]; 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[-6] = z[-6] + z[-14];
z[-7] = z[-7] + z[-15]; z[-7] = z[-7] + z[-15];
z[-14] = (k00 + k11) * A2; z[-12] = k11;
z[-15] = (k00 - k11) * A2; z[-13] = -k00;
z[-14] = (l11 - l00) * A2;
z[-15] = (l00 + l11) * -A2;
iter_54(z); iter_54(z);
iter_54(z - 8); iter_54(z - 8);
@ -2630,7 +2613,8 @@ void inverse_mdct_naive(float *buffer, int n)
#endif #endif
static float *get_window(vorb *f, int len) { 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_0) return f->window[0];
if (len == f->blocksize_1) return f->window[1]; if (len == f->blocksize_1) return f->window[1];
return NULL; return NULL;
@ -2755,8 +2739,8 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start,
int right_end, int *p_left) { int right_end, int *p_left) {
Mapping *map; Mapping *map;
int i, j, k, n, n2; int i, j, k, n, n2;
int zero_channel[256]; int zero_channel[256] = {0};
int really_zero_channel[256]; int really_zero_channel[256] = {0};
// WINDOWING // WINDOWING
@ -2959,7 +2943,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 // this isn't to spec, but spec would require us to read ahead
// and decode the size of all current frames--could be done, // and decode the size of all current frames--could be done,
// but presumably it's not a commonly used feature // 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, // we might have to discard samples "from" the next frame too,
// if we're lapping a large block then a small at the start? // if we're lapping a large block then a small at the start?
f->discard_samples_deferred = n - right_end; f->discard_samples_deferred = n - right_end;
@ -3089,7 +3075,7 @@ static int vorbis_pump_first_frame(stb_vorbis *f) {
} }
#ifndef STB_VORBIS_NO_PUSHDATA_API #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... // 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 // this requires a full ogg parse, but we know we can fetch from f->stream
@ -3109,8 +3095,6 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page) {
break; break;
} }
// either this continues, or it ends it... // 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 (s == f->segment_count) s = -1; // set 'crosses page' flag
if (p > f->stream_end) return error(f, VORBIS_need_more_data); if (p > f->stream_end) return error(f, VORBIS_need_more_data);
first = FALSE; first = FALSE;
@ -3144,8 +3128,6 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page) {
p += q[s]; p += q[s];
if (q[s] < 255) break; 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 (s == n) s = -1; // set 'crosses page' flag
if (p > f->stream_end) return error(f, VORBIS_need_more_data); if (p > f->stream_end) return error(f, VORBIS_need_more_data);
first = FALSE; first = FALSE;
@ -3160,6 +3142,7 @@ static int start_decoder(vorb *f) {
int longest_floorlist = 0; int longest_floorlist = 0;
// first page, first packet // first page, first packet
f->first_decode = TRUE;
if (!start_page(f)) return FALSE; if (!start_page(f)) return FALSE;
// validate page flag // validate page flag
@ -3218,6 +3201,50 @@ static int start_decoder(vorb *f) {
if (!start_page(f)) return FALSE; if (!start_page(f)) return FALSE;
if (!start_packet(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 { do {
len = next_segment(f); len = next_segment(f);
skip(f, len); skip(f, len);
@ -3229,7 +3256,7 @@ static int start_decoder(vorb *f) {
#ifndef STB_VORBIS_NO_PUSHDATA_API #ifndef STB_VORBIS_NO_PUSHDATA_API
if (IS_PUSH_MODE(f)) { 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 // convert error in ogg header to write type
if (f->error == VORBIS_invalid_stream) f->error = VORBIS_invalid_setup; if (f->error == VORBIS_invalid_stream) f->error = VORBIS_invalid_setup;
return FALSE; return FALSE;
@ -3302,7 +3329,10 @@ static int start_decoder(vorb *f) {
if (present) { if (present) {
lengths[j] = get_bits(f, 5) + 1; lengths[j] = get_bits(f, 5) + 1;
++total; ++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 { } else {
lengths[j] = NO_CODE; lengths[j] = NO_CODE;
} }
@ -3315,7 +3345,10 @@ static int start_decoder(vorb *f) {
f->setup_temp_memory_required = c->entries; f->setup_temp_memory_required = c->entries;
c->codeword_lengths = (uint8 *)setup_malloc(f, 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); memcpy(c->codeword_lengths, lengths, c->entries);
setup_temp_free(f, lengths, setup_temp_free(f, lengths,
c->entries); // note this is only safe if there have been c->entries); // note this is only safe if there have been
@ -3349,13 +3382,22 @@ static int start_decoder(vorb *f) {
unsigned int size; unsigned int size;
if (c->sorted_entries) { if (c->sorted_entries) {
c->codeword_lengths = (uint8 *)setup_malloc(f, 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( c->codewords = (uint32 *)setup_temp_malloc(
f, sizeof(*c->codewords) * c->sorted_entries); 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 = values =
(uint32 *)setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); (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 + size = c->entries +
(sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries;
@ -3364,7 +3406,10 @@ static int start_decoder(vorb *f) {
} }
if (!compute_codewords(c, lengths, c->entries, values)) { 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); return error(f, VORBIS_invalid_setup);
} }
@ -3372,12 +3417,18 @@ static int start_decoder(vorb *f) {
// allocate an extra slot for sentinels // allocate an extra slot for sentinels
c->sorted_codewords = (uint32 *)setup_malloc( c->sorted_codewords = (uint32 *)setup_malloc(
f, sizeof(*c->sorted_codewords) * (c->sorted_entries + 1)); 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 // 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 // defined so that we can catch that case without an extra if
c->sorted_values = (int *)setup_malloc( c->sorted_values = (int *)setup_malloc(
f, sizeof(*c->sorted_values) * (c->sorted_entries + 1)); 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;
c->sorted_values[-1] = -1; c->sorted_values[-1] = -1;
compute_sorted_huffman(c, lengths, values); compute_sorted_huffman(c, lengths, values);
@ -3446,8 +3497,7 @@ static int start_decoder(vorb *f) {
unsigned int div = 1; unsigned int div = 1;
for (k = 0; k < c->dimensions; ++k) { for (k = 0; k < c->dimensions; ++k) {
int off = (z / div) % c->lookup_values; int off = (z / div) % c->lookup_values;
float val = mults[off]; float val = mults[off] * c->delta_value + c->minimum_value + last;
val = mults[off] * c->delta_value + c->minimum_value + last;
c->multiplicands[j * c->dimensions + k] = val; c->multiplicands[j * c->dimensions + k] = val;
if (c->sequence_p) last = val; if (c->sequence_p) last = val;
if (k + 1 < c->dimensions) { if (k + 1 < c->dimensions) {
@ -3532,7 +3582,7 @@ static int start_decoder(vorb *f) {
return error(f, VORBIS_invalid_setup); return error(f, VORBIS_invalid_setup);
} }
for (k = 0; k < 1 << g->class_subclasses[j]; ++k) { 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) if (g->subclass_books[j][k] >= f->codebook_count)
return error(f, VORBIS_invalid_setup); return error(f, VORBIS_invalid_setup);
} }
@ -3560,7 +3610,7 @@ static int start_decoder(vorb *f) {
for (j = 0; j < g->values; ++j) g->sorted_order[j] = (uint8)p[j].id; for (j = 0; j < g->values; ++j) g->sorted_order[j] = (uint8)p[j].id;
// precompute the neighbors // precompute the neighbors
for (j = 2; j < g->values; ++j) { for (j = 2; j < g->values; ++j) {
int low, hi; int low = 0, hi = 0;
neighbors(g->Xlist, j, &low, &hi); neighbors(g->Xlist, j, &low, &hi);
g->neighbors[j][0] = low; g->neighbors[j][0] = low;
g->neighbors[j][1] = hi; g->neighbors[j][1] = hi;
@ -3738,7 +3788,9 @@ static int start_decoder(vorb *f) {
int i, max_part_read = 0; int i, max_part_read = 0;
for (i = 0; i < f->residue_count; ++i) { for (i = 0; i < f->residue_count; ++i) {
Residue *r = f->residue_config + 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 = unsigned int limit_r_begin =
r->begin < actual_size ? r->begin : actual_size; r->begin < actual_size ? r->begin : actual_size;
unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size;
@ -3761,8 +3813,6 @@ static int start_decoder(vorb *f) {
f->temp_memory_required = imdct_mem; f->temp_memory_required = imdct_mem;
} }
f->first_decode = TRUE;
if (f->alloc.alloc_buffer) { if (f->alloc.alloc_buffer) {
assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes);
// check if there's enough temp memory so we don't error later // check if there's enough temp memory so we don't error later
@ -3771,13 +3821,30 @@ static int start_decoder(vorb *f) {
return error(f, VORBIS_outofmem); 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; return TRUE;
} }
static void vorbis_deinit(stb_vorbis *p) { static void vorbis_deinit(stb_vorbis *p) {
int i, j; 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) { if (p->residue_config) {
for (i = 0; i < p->residue_count; ++i) { for (i = 0; i < p->residue_count; ++i) {
Residue *r = p->residue_config + i; Residue *r = p->residue_config + i;
@ -3840,8 +3907,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 memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start
if (z) { if (z) {
p->alloc = *z; p->alloc = *z;
p->alloc.alloc_buffer_length_in_bytes = p->alloc.alloc_buffer_length_in_bytes &= ~7;
(p->alloc.alloc_buffer_length_in_bytes + 3) & ~3;
p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; p->temp_offset = p->alloc.alloc_buffer_length_in_bytes;
} }
p->eof = 0; p->eof = 0;
@ -3873,6 +3939,14 @@ stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) {
return d; 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 stb_vorbis_get_error(stb_vorbis *f) {
int e = f->error; int e = f->error;
f->error = VORBIS__no_error; f->error = VORBIS__no_error;
@ -4007,7 +4081,7 @@ int stb_vorbis_decode_frame_pushdata(
f->error = VORBIS__no_error; f->error = VORBIS__no_error;
// check that we have the entire packet in memory // check that we have the entire packet in memory
if (!is_whole_packet_present(f, FALSE)) { if (!is_whole_packet_present(f)) {
*samples = 0; *samples = 0;
return 0; return 0;
} }
@ -4069,6 +4143,7 @@ stb_vorbis *stb_vorbis_open_pushdata(
*error = VORBIS_need_more_data; *error = VORBIS_need_more_data;
else else
*error = p.error; *error = p.error;
vorbis_deinit(&p);
return NULL; return NULL;
} }
f = vorbis_alloc(&p); f = vorbis_alloc(&p);
@ -4121,7 +4196,7 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) {
if (f->eof) return 0; if (f->eof) return 0;
if (header[4] != 0) goto invalid; if (header[4] != 0) goto invalid;
goal = header[22] + (header[23] << 8) + (header[24] << 16) + 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; for (i = 22; i < 26; ++i) header[i] = 0;
crc = 0; crc = 0;
for (i = 0; i < 27; ++i) crc = crc32_update(crc, header[i]); for (i = 0; i < 27; ++i) crc = crc32_update(crc, header[i]);
@ -4232,8 +4307,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) { static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) {
ProbedPage left, right, mid; ProbedPage left, right, mid;
int i, start_seg_with_known_loc, end_pos, page_start; int i, start_seg_with_known_loc, end_pos, page_start;
uint32 delta, stream_length, padding; uint32 delta, stream_length, padding, last_sample_limit;
double offset, bytes_per_sample; double offset = 0.0, bytes_per_sample = 0.0;
int probe = 0; int probe = 0;
bytes_per_sample = 2; /* TODO(jart): ???? */ bytes_per_sample = 2; /* TODO(jart): ???? */
@ -4249,9 +4324,9 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) {
// indicates should be the granule position (give or take one)). // indicates should be the granule position (give or take one)).
padding = ((f->blocksize_1 - f->blocksize_0) >> 2); padding = ((f->blocksize_1 - f->blocksize_0) >> 2);
if (sample_number < padding) if (sample_number < padding)
sample_number = 0; last_sample_limit = 0;
else else
sample_number -= padding; last_sample_limit = sample_number - padding;
left = f->p_first; left = f->p_first;
while (left.last_decoded_sample == ~0U) { while (left.last_decoded_sample == ~0U) {
@ -4264,8 +4339,11 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) {
assert(right.last_decoded_sample != ~0U); assert(right.last_decoded_sample != ~0U);
// starting from the start is handled differently // starting from the start is handled differently
if (sample_number <= left.last_decoded_sample) { if (last_sample_limit <= left.last_decoded_sample) {
if (stb_vorbis_seek_start(f)) return 1; if (stb_vorbis_seek_start(f)) {
if (f->current_loc > sample_number) return error(f, VORBIS_seek_failed);
return 1;
}
return 0; return 0;
} }
@ -4284,10 +4362,10 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) {
bytes_per_sample = data_bytes / right.last_decoded_sample; bytes_per_sample = data_bytes / right.last_decoded_sample;
offset = offset =
left.page_start + left.page_start +
bytes_per_sample * (sample_number - left.last_decoded_sample); bytes_per_sample * (last_sample_limit - left.last_decoded_sample);
} else { } else {
// second probe (try to bound the other side) // 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; bytes_per_sample;
if (error >= 0 && error < 8000) error = 8000; if (error >= 0 && error < 8000) error = 8000;
if (error < 0 && error > -8000) error = -8000; if (error < 0 && error > -8000) error = -8000;
@ -4318,13 +4396,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, // if we've just found the last page again then we're in a tricky file,
// and we're close enough. // and we're close enough (if it wasn't an interpolation probe).
if (mid.page_start == right.page_start) break; if (mid.page_start == right.page_start) {
if (probe >= 2 || delta <= 65536) break;
if (sample_number < mid.last_decoded_sample) } else {
right = mid; if (last_sample_limit < mid.last_decoded_sample)
else right = mid;
left = mid; else
left = mid;
}
++probe; ++probe;
} }
@ -4437,8 +4517,8 @@ int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) {
flush_packet(f); flush_packet(f);
} }
} }
// the next frame will start with the sample // the next frame should start with the sample
assert(f->current_loc == sample_number); if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed);
return 1; return 1;
} }
@ -4514,7 +4594,8 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) {
// set. whoops! // set. whoops!
break; 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); last_page_loc = stb_vorbis_get_file_offset(f);
} }
@ -4618,7 +4699,10 @@ stb_vorbis *stb_vorbis_open_filename(const char *filename, int *error,
stb_vorbis *stb_vorbis_open_memory(const unsigned char *data, int len, stb_vorbis *stb_vorbis_open_memory(const unsigned char *data, int len,
int *error, const stb_vorbis_alloc *alloc) { int *error, const stb_vorbis_alloc *alloc) {
stb_vorbis *f, p; stb_vorbis *f, p;
if (data == NULL) return NULL; if (!data) {
if (error) *error = VORBIS_unexpected_eof;
return NULL;
}
vorbis_init(&p, alloc); vorbis_init(&p, alloc);
p.stream = (uint8 *)data; p.stream = (uint8 *)data;
p.stream_end = (uint8 *)data + len; p.stream_end = (uint8 *)data + len;
@ -4684,18 +4768,18 @@ static void copy_samples(short *dest, float *src, int len) {
for (i = 0; i < len; ++i) { for (i = 0; i < len; ++i) {
FASTDEF(temp); FASTDEF(temp);
int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i], 15); 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; dest[i] = v;
} }
} }
static void compute_samples(int mask, short *output, int num_c, float **data, static void compute_samples(int mask, short *output, int num_c, float **data,
int d_offset, int len) { int d_offset, int len) {
#define BUFFER_SIZE 32 #define STB_BUFFER_SIZE 32
float buffer[BUFFER_SIZE]; float buffer[STB_BUFFER_SIZE];
int i, j, o, n = BUFFER_SIZE; int i, j, o, n = STB_BUFFER_SIZE;
check_endianness(); check_endianness();
for (o = 0; o < len; o += BUFFER_SIZE) { for (o = 0; o < len; o += STB_BUFFER_SIZE) {
memset(buffer, 0, sizeof(buffer)); memset(buffer, 0, sizeof(buffer));
if (o + n > len) n = len - o; if (o + n > len) n = len - o;
for (j = 0; j < num_c; ++j) { for (j = 0; j < num_c; ++j) {
@ -4706,20 +4790,21 @@ static void compute_samples(int mask, short *output, int num_c, float **data,
for (i = 0; i < n; ++i) { for (i = 0; i < n; ++i) {
FASTDEF(temp); FASTDEF(temp);
int v = FAST_SCALED_FLOAT_TO_INT(temp, buffer[i], 15); 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; output[o + i] = v;
} }
} }
#undef STB_BUFFER_SIZE
} }
static void compute_stereo_samples(short *output, int num_c, float **data, static void compute_stereo_samples(short *output, int num_c, float **data,
int d_offset, int len) { int d_offset, int len) {
#define BUFFER_SIZE 32 #define STB_BUFFER_SIZE 32
float buffer[BUFFER_SIZE]; float buffer[STB_BUFFER_SIZE];
int i, j, o, n = BUFFER_SIZE >> 1; int i, j, o, n = STB_BUFFER_SIZE >> 1;
// o is the offset in the source data // o is the offset in the source data
check_endianness(); 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 // o2 is the offset in the output data
int o2 = o << 1; int o2 = o << 1;
memset(buffer, 0, sizeof(buffer)); memset(buffer, 0, sizeof(buffer));
@ -4744,10 +4829,11 @@ static void compute_stereo_samples(short *output, int num_c, float **data,
for (i = 0; i < (n << 1); ++i) { for (i = 0; i < (n << 1); ++i) {
FASTDEF(temp); FASTDEF(temp);
int v = FAST_SCALED_FLOAT_TO_INT(temp, buffer[i], 15); 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; output[o2 + i] = v;
} }
} }
#undef STB_BUFFER_SIZE
} }
static void convert_samples_short(int buf_c, short **buffer, int b_offset, static void convert_samples_short(int buf_c, short **buffer, int b_offset,
@ -4771,7 +4857,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 stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer,
int num_samples) { int num_samples) {
float **output; float **output = NULL;
int len = stb_vorbis_get_frame_float(f, NULL, &output); int len = stb_vorbis_get_frame_float(f, NULL, &output);
if (len > num_samples) len = num_samples; if (len > num_samples) len = num_samples;
if (len) convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); if (len) convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len);
@ -4796,7 +4882,7 @@ static void convert_channels_short_interleaved(int buf_c, short *buffer,
float f = data[i][d_offset + j]; float f = data[i][d_offset + j];
int v = int v =
FAST_SCALED_FLOAT_TO_INT(temp, f, 15); // data[i][d_offset+j],15); 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; *buffer++ = v;
} }
for (; i < buf_c; ++i) *buffer++ = 0; for (; i < buf_c; ++i) *buffer++ = 0;
@ -4824,8 +4910,6 @@ int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels,
float **outputs; float **outputs;
int len = num_shorts / channels; int len = num_shorts / channels;
int n = 0; int n = 0;
int z = f->channels;
if (z > channels) z = channels;
while (n < len) { while (n < len) {
int k = f->channel_buffer_end - f->channel_buffer_start; int k = f->channel_buffer_end - f->channel_buffer_start;
if (n + k >= len) k = len - n; if (n + k >= len) k = len - n;
@ -4846,8 +4930,6 @@ int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer,
int len) { int len) {
float **outputs; float **outputs;
int n = 0; int n = 0;
int z = f->channels;
if (z > channels) z = channels;
while (n < len) { while (n < len) {
int k = f->channel_buffer_end - f->channel_buffer_start; int k = f->channel_buffer_end - f->channel_buffer_start;
if (n + k >= len) k = len - n; if (n + k >= len) k = len - n;

View file

@ -43,9 +43,18 @@ typedef struct {
int max_frame_size; int max_frame_size;
} stb_vorbis_info; } stb_vorbis_info;
typedef struct {
char *vendor;
int comment_list_length;
char **comment_list;
} stb_vorbis_comment;
// get general information about the file // get general information about the file
stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); 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) // get the last error detected (clears it, too)
int stb_vorbis_get_error(stb_vorbis *f); int stb_vorbis_get_error(stb_vorbis *f);
@ -119,6 +128,12 @@ int stb_vorbis_decode_frame_pushdata(
// channel. In other words, (*output)[0][0] contains the first sample from // 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 first channel, and (*output)[1][0] contains the first sample from
// the second channel. // 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); void stb_vorbis_flush_pushdata(stb_vorbis *f);
// inform stb_vorbis that your next datablock will not be contiguous with // inform stb_vorbis that your next datablock will not be contiguous with

View file

@ -551,8 +551,8 @@ static int ParseNumberOption(const char *arg) {
return x; return x;
} }
static void PrintUsage(int rc, FILE *f) { static void PrintUsage(int rc, int fd) {
fputs(HELPTEXT, f); tinyprint(fd, HELPTEXT, NULL);
exit(rc); exit(rc);
} }
@ -573,9 +573,12 @@ static void GetOpts(int argc, char *argv[]) {
break; break;
case '?': case '?':
case 'H': case 'H':
PrintUsage(EXIT_SUCCESS, stdout);
default: default:
PrintUsage(EX_USAGE, stderr); if (opt == optopt) {
PrintUsage(EXIT_SUCCESS, STDOUT_FILENO);
} else {
PrintUsage(EX_USAGE, STDERR_FILENO);
}
} }
} }
} }

View file

@ -45,6 +45,7 @@
#include "libc/str/unicode.h" #include "libc/str/unicode.h"
#include "libc/sysv/consts/ex.h" #include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h" #include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h" #include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/poll.h"
@ -63,7 +64,7 @@ DESCRIPTION\n\
\n\ \n\
FLAGS\n\ FLAGS\n\
\n\ \n\
-h help\n\ -h or -? help\n\
-z zoom\n\ -z zoom\n\
-m morton ordering\n\ -m morton ordering\n\
-H hilbert ordering\n\ -H hilbert ordering\n\
@ -887,10 +888,8 @@ static void MemZoom(void) {
} while (!(action & INTERRUPTED)); } while (!(action & INTERRUPTED));
} }
static wontreturn void PrintUsage(int rc) { static wontreturn void PrintUsage(int rc, int fd) {
Write("SYNOPSIS\n\n "); tinyprint(fd, "SYNOPSIS\n\n ", program_invocation_name, USAGE, NULL);
Write(program_invocation_name);
Write(USAGE);
exit(rc); exit(rc);
} }
@ -898,7 +897,7 @@ static void GetOpts(int argc, char *argv[]) {
int opt; int opt;
char *p; char *p;
fps = 10; fps = 10;
while ((opt = getopt(argc, argv, "hzHNWf:p:")) != -1) { while ((opt = getopt(argc, argv, "?hmzHNWf:p:")) != -1) {
switch (opt) { switch (opt) {
case 'z': case 'z':
++zoom; ++zoom;
@ -927,9 +926,13 @@ static void GetOpts(int argc, char *argv[]) {
} }
break; break;
case 'h': case 'h':
PrintUsage(EXIT_SUCCESS); case '?':
default: default:
PrintUsage(EX_USAGE); if (opt == optopt) {
PrintUsage(EXIT_SUCCESS, STDOUT_FILENO);
} else {
PrintUsage(EX_USAGE, STDERR_FILENO);
}
} }
} }
if (pid) { if (pid) {
@ -941,10 +944,10 @@ static void GetOpts(int argc, char *argv[]) {
stpcpy(p, "/maps"); stpcpy(p, "/maps");
} else { } else {
if (optind == argc) { if (optind == argc) {
PrintUsage(EX_USAGE); PrintUsage(EX_USAGE, STDERR_FILENO);
} }
if (!memccpy(path, argv[optind], '\0', sizeof(path))) { if (!memccpy(path, argv[optind], '\0', sizeof(path))) {
PrintUsage(EX_USAGE); PrintUsage(EX_USAGE, STDERR_FILENO);
} }
} }
} }

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/calls/calls.h"
#include "libc/errno.h" #include "libc/errno.h"
#include "libc/fmt/conv.h" #include "libc/fmt/conv.h"
#include "libc/log/check.h" #include "libc/log/check.h"
@ -25,6 +26,7 @@
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/sysv/consts/ex.h" #include "libc/sysv/consts/ex.h"
#include "libc/sysv/consts/exit.h" #include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/fileno.h"
#include "third_party/getopt/getopt.internal.h" #include "third_party/getopt/getopt.internal.h"
#define USAGE \ #define USAGE \
@ -36,17 +38,15 @@ Flags:\n\
-c INT\n\ -c INT\n\
-w INT width (aka cols) [default 8]\n\ -w INT width (aka cols) [default 8]\n\
-o PATH output path [default -]\n\ -o PATH output path [default -]\n\
-h shows this information\n\ -h or -? shows this information\n\
\n" \n"
static long width_; static long width_;
static FILE *in_, *out_; static FILE *in_, *out_;
static char *inpath_, *outpath_; static char *inpath_, *outpath_;
void PrintUsage(int rc, FILE *f) { void PrintUsage(int rc, int fd) {
fputs("Usage: ", f); tinyprint(fd, "Usage: ", program_invocation_name, USAGE, NULL);
fputs(program_invocation_name, f);
fputs(USAGE, f);
exit(rc); exit(rc);
} }
@ -63,11 +63,14 @@ void GetOpts(int *argc, char *argv[]) {
case 'w': case 'w':
width_ = strtol(optarg, NULL, 0); width_ = strtol(optarg, NULL, 0);
break; break;
case '?':
case 'h': case 'h':
PrintUsage(EXIT_SUCCESS, stdout); case '?':
default: default:
PrintUsage(EX_USAGE, stderr); if (opt == optopt) {
PrintUsage(EXIT_SUCCESS, STDOUT_FILENO);
} else {
PrintUsage(EX_USAGE, STDERR_FILENO);
}
} }
} }
if (optind == *argc) { if (optind == *argc) {

View file

@ -71,8 +71,8 @@ static struct Flags {
enum TtyQuantizationAlgorithm quant; enum TtyQuantizationAlgorithm quant;
} g_flags; } g_flags;
static wontreturn void PrintUsage(int rc, FILE *f) { static wontreturn void PrintUsage(int rc, int fd) {
fprintf(f, "Usage: %s%s", program_invocation_name, "\ tinyprint(fd, "Usage: ", program_invocation_name, "\
[FLAGS] [PATH]\n\ [FLAGS] [PATH]\n\
\n\ \n\
FLAGS\n\ FLAGS\n\
@ -86,7 +86,7 @@ EXAMPLES\n\
\n\ \n\
printansi.com -w80 -h40 logo.png\n\ printansi.com -w80 -h40 logo.png\n\
\n\ \n\
\n"); \n", NULL);
exit(rc); exit(rc);
} }
@ -107,7 +107,7 @@ static void GetOpts(int *argc, char *argv[]) {
g_flags.blocks = IsWindows() ? kTtyBlocksCp437 : kTtyBlocksUnicode; g_flags.blocks = IsWindows() ? kTtyBlocksCp437 : kTtyBlocksUnicode;
if (*argc == 2 && if (*argc == 2 &&
(strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0)) { (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) { while ((opt = getopt(*argc, argv, "?ivpfrtxads234o:w:h:")) != -1) {
switch (opt) { switch (opt) {
@ -162,9 +162,12 @@ static void GetOpts(int *argc, char *argv[]) {
++__log_level; ++__log_level;
break; break;
case '?': case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default: default:
PrintUsage(EX_USAGE, stderr); if (opt == optopt) {
PrintUsage(EXIT_SUCCESS, STDOUT_FILENO);
} else {
PrintUsage(EX_USAGE, STDERR_FILENO);
}
} }
} }
if (optind == *argc) { if (optind == *argc) {

View file

@ -66,8 +66,8 @@ static struct Flags {
struct winsize g_winsize; struct winsize g_winsize;
static wontreturn void PrintUsage(int rc, FILE *f) { static wontreturn void PrintUsage(int rc, int fd) {
fprintf(f, "Usage: %s%s", program_invocation_name, "\ tinyprint(fd, "Usage: ", program_invocation_name, "\
[FLAGS] [PATH]\n\ [FLAGS] [PATH]\n\
\n\ \n\
FLAGS\n\ FLAGS\n\
@ -94,7 +94,7 @@ FLAGS\n\
EXAMPLES\n\ EXAMPLES\n\
\n\ \n\
printimage.com -sxd lemurs.jpg # 256-color dither unsharp\n\ printimage.com -sxd lemurs.jpg # 256-color dither unsharp\n\
\n"); \n", NULL);
exit(rc); exit(rc);
} }
@ -114,7 +114,7 @@ static void GetOpts(int *argc, char *argv[]) {
g_flags.blocks = IsWindows() ? kTtyBlocksCp437 : kTtyBlocksUnicode; g_flags.blocks = IsWindows() ? kTtyBlocksCp437 : kTtyBlocksUnicode;
if (*argc == 2 && if (*argc == 2 &&
(strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0)) { (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) { while ((opt = getopt(*argc, argv, "?vpmfirtxads234o:w:h:")) != -1) {
switch (opt) { switch (opt) {
@ -170,9 +170,12 @@ static void GetOpts(int *argc, char *argv[]) {
++__log_level; ++__log_level;
break; break;
case '?': case '?':
PrintUsage(EXIT_SUCCESS, stdout);
default: default:
PrintUsage(EX_USAGE, stderr); if (opt == optopt) {
PrintUsage(EXIT_SUCCESS, STDOUT_FILENO);
} else {
PrintUsage(EX_USAGE, STDERR_FILENO);
}
} }
} }
g_winsize.ws_col = 80; g_winsize.ws_col = 80;
@ -435,7 +438,7 @@ int main(int argc, char *argv[]) {
int i; int i;
ShowCrashReports(); ShowCrashReports();
GetOpts(&argc, argv); GetOpts(&argc, argv);
if (optind == argc) PrintUsage(0, stdout); if (optind == argc) PrintUsage(EXIT_SUCCESS, STDOUT_FILENO);
stbi_set_unpremultiply_on_load(true); stbi_set_unpremultiply_on_load(true);
for (i = optind; i < argc; ++i) { for (i = optind; i < argc; ++i) {
WithImageFile(argv[i], ProcessImage); WithImageFile(argv[i], ProcessImage);

View file

@ -123,7 +123,7 @@ Flags & Keyboard Shortcuts:\n\
-v increases verbosity [flag]\n\ -v increases verbosity [flag]\n\
-L PATH redirects stderr to path [flag]\n\ -L PATH redirects stderr to path [flag]\n\
-y yes to interactive prompts [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\ UP/DOWN adjust volume [keyboard]\n\
CTRL+L redraw [keyboard]\n\ CTRL+L redraw [keyboard]\n\
CTRL+Z suspend [keyboard]\n\ CTRL+Z suspend [keyboard]\n\
@ -1374,10 +1374,8 @@ static bool CanPlayAudio(void) {
} }
} }
static void PrintUsage(int rc, FILE *f) { static void PrintUsage(int rc, int fd) {
fputs("Usage: ", f); tinyprint(fd, "Usage: ", program_invocation_name, USAGE, NULL);
fputs(program_invocation_name, f);
fputs(USAGE, f);
exit(rc); exit(rc);
} }
@ -1399,12 +1397,15 @@ static void GetOpts(int argc, char *argv[]) {
case 'Y': case 'Y':
yonly_ = true; yonly_ = true;
break; break;
case '?':
case 'h': case 'h':
PrintUsage(EXIT_SUCCESS, stdout); case '?':
default: default:
if (!ProcessOptKey(opt)) { if (!ProcessOptKey(opt)) {
PrintUsage(EX_USAGE, stderr); if (opt == optopt) {
PrintUsage(EXIT_SUCCESS, STDOUT_FILENO);
} else {
PrintUsage(EX_USAGE, STDERR_FILENO);
}
} }
} }
} }
@ -1562,7 +1563,7 @@ int main(int argc, char *argv[]) {
fullclear_ = true; fullclear_ = true;
GetOpts(argc, argv); GetOpts(argc, argv);
if (!tuned_) PickDefaults(); if (!tuned_) PickDefaults();
if (optind == argc) PrintUsage(EX_USAGE, stderr); if (optind == argc) PrintUsage(EX_USAGE, STDERR_FILENO);
patharg_ = argv[optind]; patharg_ = argv[optind];
s = commandvenv("SOX", "sox"); s = commandvenv("SOX", "sox");
sox_ = s ? strdup(s) : 0; sox_ = s ? strdup(s) : 0;