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

View file

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

View file

@ -5,8 +5,8 @@ LOCAL CHANGES
- Removed undefined behavior
- Removed BMP [endian code made it 100x slower than PNG/JPEG]
- Removed PIC [never heard of it]
- Removed TGA [consider imaagemagick convert command]
- Removed PSD [consider imaagemagick convert command]
- Removed TGA [consider imagemagick convert command]
- Removed PSD [consider imagemagick convert command]
- Removed HDR [mine eyes and wikipedia agree stb gamma math is off]
- Patched PNG loading edge case
- Fixed code C standard says is undefined
@ -14,10 +14,25 @@ LOCAL CHANGES
- Removed unnecessary ifdefs
- Removed MSVC torture code
SYNCHRONIZATION POINT
SYNCHRONIZATION POINT (`--date=format:"%a %b %d %H:%M:%S %Y %z"`)
commit f67165c2bb2af3060ecae7d20d6f731173485ad0
Author: Sean Barrett <sean2@nothings.org>
Date: Mon Oct 28 09:30:02 2019 -0700
commit 5736b15f7ea0ffb08dd38af21067c314d6a3aae9
Author: Sean Barrett <seanb@radgametools.com>
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.23 - public domain image loader - http://nothings.org/stb
/* stb_image - v2.29 - public domain image loader - http://nothings.org/stb
* no warranty implied; use at your own risk
*
* [heavily modified by justine tunney]
*
* JPEG baseline & progressive (12 bpc/arithmetic not supported, same
* as stock IJG lib) PNG 1/2/4/8/16-bit-per-channel
* as stock IJG lib)
* PNG 1/2/4/8/16-bit-per-channel
* GIF (*comp always reports as 4-channel)
* HDR (radiance rgbE format)
* PNM (PPM and PGM binary only)
*
* Animated GIF still needs a proper API, but here's one way to do it:
@ -18,45 +17,53 @@
*
* ============================ Contributors =========================
*
* Image formats Extensions, features
* Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info)
* Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info)
* Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG)
* Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks)
* Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG)
* Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip)
* Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD)
* github:urraka (animated gif) Junggon Kim (PNM comments)
* Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA)
* socks-the-fox (16-bit PNG)
* Jeremy Sawicki (ImageNet JPGs)
* Mikhail Morozov (1-bit BMP)
* Optimizations & bugfixes Anael Seghezzi (is-16-bit query)
* Fabian "ryg" Giesen
* Arseny Kapoulkine
* John-Mark Allen
* Image formats Extensions, features
* Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info)
* Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info)
* Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG)
* Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks)
* Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG)
* Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip)
* Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD)
* github:urraka (animated gif) Junggon Kim (PNM comments)
* Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA)
* socks-the-fox (16-bit PNG)
* Optimizations & bugfixes Jeremy Sawicki (ImageNet JPGs)
* Fabian "ryg" Giesen Mikhail Morozov (1-bit BMP)
* Arseny Kapoulkine Anael Seghezzi (is-16-bit query)
* John-Mark Allen Simon Breuss (16-bit PNM)
* Carmelo J Fdez-Aguera
*
* Bug & warning fixes
* Marc LeBlanc David Woo Guillaume George Martins Mozeiko
* Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan
* Dave Moore Roy Eltham Hayaki Saito Nathan Reed
* Won Chun Luke Graham Johan Duparc Nick Verigakis
* the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh
* Janez Zemva John Bartholomew Michal Cichon github:romigrou
* Jonathan Blow Ken Hamada Tero Hanninen github:svdijk
* Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar
* Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex
* Ryamond Barbiero Paul Du Bois Engin Manap github:grim210
* Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw
* Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus
* Julian Raschke Gregory Mullen Baldur Karlsson
* github:poppolopoppo Christian Floisand Kevin Schmidt JR Smith
* github:darealshinji Blazej Dariusz Roszkowski github:Michaelangel007
*/
/*
* DOCUMENTATION
* Marc LeBlanc Laurent Gomila JR Smith
* Christpher Lloyd Sergio Gonzalez Matvey Cherevko
* Phil Jordan Ryamond Barbiero Zack Middleton
* Hayaki Saito Engin Manap
* Luke Graham Dale Weiler Martins Mozeiko
* Thomas Ruf Neil Bickford Blazej Dariusz Roszkowski
* Janez Zemva Gregory Mullen Roy Eltham
* Jonathan Blow Kevin Schmidt
* Eugene Golushkov Brad Weinberger the Horde3D community
* Aruelien Pocheville Alexander Veselov github:rlyeh
* Cass Everitt [reserved] github:romigrou
* Paul Du Bois github:svdijk
* Philipp Wiesemann Guillaume George github:snagar
* Josh Tobin Joseph Thomson github:Zelex
* Julian Raschke Dave Moore github:grim210
* Baldur Karlsson Won Chun github:sammyhw
* Nick Verigakis github:phprus
* Luca Sas github:poppolopoppo
* Ryan C. Gordon Michal Cichon github:darealshinji
* David Woo Tero Hanninen github:Michaelangel007
* Jerry Jansson Cort Stratton github:mosra
* Thibault Reuille [reserved]
* Nathan Reed [reserved]
* Johan Duparc Aldo Culquicondor
* Ronny Chevalier Oriol Ferrer Jacko Dirks
* John Bartholomew Matthew Gregan
* Ken Hamada Christian Floisand
*
* ============================ Documentation =========================
*
* Limitations:
* - no 12-bit-per-channel JPEG
@ -70,14 +77,15 @@
* // ... x = width, y = height, n = # 8-bit components per pixel ...
* // ... replace '0' with '1'..'4' to force that many components per pixel
* // ... but 'n' will always be the number that it would have been if you
* said 0 stbi_image_free(data)
* // ... said 0
* stbi_image_free(data);
*
* Standard parameters:
* int *x -- outputs image width in pixels
* int *y -- outputs image height in pixels
* int *channels_in_file -- outputs # of image components in image file
* int desired_channels -- if non-zero, # of image components requested in
* result
* result
*
* The return value from an image loader is an 'unsigned char *' which points
* to the pixel data, or NULL on an allocation failure or if the image is
@ -110,6 +118,32 @@
*
* Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.
*
* To query the width, height and component count of an image without having to
* decode the full file, you can use the stbi_info family of functions:
*
* int x,y,n,ok;
* ok = stbi_info(filename, &x, &y, &n);
* // returns ok=1 and sets x, y, n if image is a supported format,
* // 0 otherwise.
*
* Note that stb_image pervasively uses ints in its public API for sizes,
* including sizes of memory buffers. This is now part of the API and thus
* hard to change without causing breakage. As a result, the various image
* loaders all have certain limits on image size; these differ somewhat
* by format but generally boil down to either just under 2GB or just under
* 1GB. When the decoded image would be larger than this, stb_image decoding
* will fail.
*
* Additionally, stb_image will reject image files that have any of their
* dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS,
* which defaults to 2**24 = 16777216 pixels. Due to the above memory limit,
* the only way to have an image with such dimensions load correctly
* is for it to have a rather extreme aspect ratio. Either way, the
* assumption here is that such larger images are likely to be malformed
* or malicious. If you do need to load an image with individual dimensions
* larger than that, and it still fits in the overall size limit, you can
* #define STBI_MAX_DIMENSIONS on your own to be something larger.
*
* ===========================================================================
*
* I/O callbacks
@ -163,11 +197,10 @@
*
* iPhone PNG support:
*
* By default we convert iphone-formatted PNGs back to RGB, even though
* they are internally encoded differently. You can disable this conversion
* by calling stbi_convert_iphone_png_to_rgb(0), in which case
* you will always just get the native iphone "format" through (which
* is BGR stored in RGB).
* We optionally support converting iPhone-formatted PNGs (which store
* premultiplied BGRA) back to RGB, even though they're internally encoded
* differently. To enable this conversion, call
* stbi_convert_iphone_png_to_rgb(1).
*
* Call stbi_set_unpremultiply_on_load(1) as well to force a divide per
* pixel to remove any premultiplied alpha *only* if the image file explicitly
@ -191,9 +224,18 @@
* - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still
* want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB
*
* - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater
* than that size (in either width or height) without further processing.
* This is to let programs in the wild set an upper bound to prevent
* denial-of-service attacks on untrusted data, as one could generate a
* valid image of gigantic dimensions and force stb_image to allocate a
* huge block of memory and spend disproportionate time decoding it. By
* default this is set to (1 << 24), which is 16777216, but that's still
* very big.
*
*/
/* stb_image_resize - v0.96 - public domain image resizing
/* stb_image_resize - v0.97 - public domain image resizing
* by Jorge L Rodriguez (@VinoBS) - 2014
* http://github.com/nothings/stb
*
@ -214,9 +256,7 @@
* output_pixels, out_w, out_h, 0,
* num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP)
* // WRAP/REFLECT/ZERO
*/
/*
*
* DOCUMENTATION
*
* SRGB & FLOATING POINT REPRESENTATION
@ -348,6 +388,7 @@
* Nathan Reed: warning fixes
*
* REVISIONS
* 0.97 (2020-02-02) fixed warning
* 0.96 (2019-03-04) fixed warnings
* 0.95 (2017-07-23) fixed warnings
* 0.94 (2017-03-18) fixed warnings

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,7 +1,7 @@
/*-*- 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
Copyright 2020 Justine Alexandra Roberts Tunney
Copyright 2023 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
@ -655,9 +655,14 @@ static void stbir__calculate_coefficients_upsample(
total_filter += coefficient_group[i];
}
STBIR_ASSERT(stbir__filter_info_table[filter].kernel(
(float)(in_last_pixel + 1) + 0.5f - in_center_of_out,
1 / scale) == 0);
// NOTE(fg): Not actually true in general, nor is there any reason to expect
// it should be. It would be true in exact math but is at best approximately
// true in floating-point math, and it would not make sense to try and put
// actual bounds on this here because it depends on the image aspect ratio
// which can get pretty extreme.
// STBIR_ASSERT(stbir__filter_info_table[filter].kernel(
// (float)(in_last_pixel + 1) + 0.5f - in_center_of_out,
// 1 / scale) == 0);
STBIR_ASSERT(total_filter > 0.9);
STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off.
@ -701,9 +706,14 @@ static void stbir__calculate_coefficients_downsample(
stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio;
}
STBIR_ASSERT(stbir__filter_info_table[filter].kernel(
(float)(out_last_pixel + 1) + 0.5f - out_center_of_in,
scale_ratio) == 0);
// NOTE(fg): Not actually true in general, nor is there any reason to expect
// it should be. It would be true in exact math but is at best approximately
// true in floating-point math, and it would not make sense to try and put
// actual bounds on this here because it depends on the image aspect ratio
// which can get pretty extreme.
// STBIR_ASSERT(stbir__filter_info_table[filter].kernel(
// (float)(out_last_pixel + 1) + 0.5f - out_center_of_in,
// scale_ratio) == 0);
for (i = out_last_pixel - out_first_pixel; i >= 0; i--) {
if (coefficient_group[i]) break;
@ -851,7 +861,7 @@ static float* stbir__get_decode_buffer(stbir__info* stbir_info) {
}
#define STBIR__DECODE(type, colorspace) \
((type) * (STBIR_MAX_COLORSPACES) + (colorspace))
((int)(type) * (STBIR_MAX_COLORSPACES) + (int)(colorspace))
static void stbir__decode_scanline(stbir__info* stbir_info, int n) {
int c;
@ -1199,7 +1209,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info,
int out_pixel_index = k * 1;
float coefficient =
horizontal_coefficients[coefficient_group + k - n0];
STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] +=
decode_buffer[in_pixel_index + 0] * coefficient;
}
@ -1220,7 +1229,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info,
int out_pixel_index = k * 2;
float coefficient =
horizontal_coefficients[coefficient_group + k - n0];
STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] +=
decode_buffer[in_pixel_index + 0] * coefficient;
output_buffer[out_pixel_index + 1] +=
@ -1243,7 +1251,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info,
int out_pixel_index = k * 3;
float coefficient =
horizontal_coefficients[coefficient_group + k - n0];
STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] +=
decode_buffer[in_pixel_index + 0] * coefficient;
output_buffer[out_pixel_index + 1] +=
@ -1268,7 +1275,6 @@ static void stbir__resample_horizontal_downsample(stbir__info* stbir_info,
int out_pixel_index = k * 4;
float coefficient =
horizontal_coefficients[coefficient_group + k - n0];
STBIR_ASSERT(coefficient != 0);
output_buffer[out_pixel_index + 0] +=
decode_buffer[in_pixel_index + 0] * coefficient;
output_buffer[out_pixel_index + 1] +=

View file

@ -1,123 +1,21 @@
/* stb_image_write - v1.13 - public domain - http://nothings.org/stb
* writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015
* no warranty implied; use at your own risk
*
* ABOUT:
*
* This file is a library for writing images to stdio or a callback.
*
* The PNG output is not optimal; it is 20-50% larger than the file
* written by a decent optimizing implementation; though providing a
* custom zlib compress function (see STBIW_ZLIB_COMPRESS) can
* mitigate that. This library is designed for source code
* compactness and simplicity, not optimal image file size or
* run-time performance.
*
* USAGE:
*
* There are five functions, one for each image file format:
*
* stbi_write_png
* stbi_write_bmp
* stbi_write_tga
* stbi_write_jpg
* stbi_write_hdr
*
* stbi_flip_vertically_on_write
*
* There are also five equivalent functions that use an arbitrary
* write function. You are expected to open/close your
* file-equivalent before and after calling these:
*
* stbi_write_png_to_func
* stbi_write_bmp_to_func
* stbi_write_tga_to_func
* stbi_write_hdr_to_func
* stbi_write_jpg_to_func
*
* where the callback is:
* void stbi_write_func(void *context, void *data, int size);
*
* You can configure it with these:
* stbi_write_tga_with_rle
* stbi_write_png_compression_level
* stbi_write_force_png_filter
*
* Each function returns 0 on failure and non-0 on success.
*
* The functions create an image file defined by the parameters. The
* image is a rectangle of pixels stored from left-to-right,
* top-to-bottom. Each pixel contains 'comp' channels of data stored
* interleaved with 8-bits per channel, in the following order: 1=Y,
* 2=YA, 3=RGB, 4=RGBA. (Y is monochrome color.) The rectangle is 'w'
* pixels wide and 'h' pixels tall. The *data pointer points to the
* first byte of the top-left-most pixel. For PNG, "stride_in_bytes"
* is the distance in bytes from the first byte of a row of pixels to
* the first byte of the next row of pixels.
*
* PNG creates output files with the same number of components as the
* input. The BMP format expands Y to RGB in the file format and does
* not output alpha.
*
* PNG supports writing rectangles of data even when the bytes
* storing rows of data are not consecutive in memory (e.g.
* sub-rectangles of a larger image), by supplying the stride between
* the beginning of adjacent rows. The other formats do not. (Thus
* you cannot write a native-format BMP through the BMP writer, both
* because it is in BGR order and because it may have padding at the
* end of the line.)
*
* PNG allows you to set the deflate compression level by setting the
* global variable 'stbi_write_png_compression_level' (it defaults to
* 8).
*
* HDR expects linear float data. Since the format is always 32-bit
* rgb(e) data, alpha (if provided) is discarded, and for monochrome
* data it is replicated across all three channels.
*
* TGA supports RLE or non-RLE compressed data. To use
* non-RLE-compressed data, set the global variable
* 'stbi_write_tga_with_rle' to 0.
*
* JPEG does ignore alpha channels in input data; quality is between
* 1 and 100. Higher quality looks better but results in a bigger
* image. JPEG baseline (no JPEG progressive).
*
* CREDITS:
*
*
* Sean Barrett - PNG/BMP/TGA
* Baldur Karlsson - HDR
* Jean-Sebastien Guay - TGA monochrome
* Tim Kelsey - misc enhancements
* Alan Hickman - TGA RLE
* Emmanuel Julien - initial file IO callback implementation
* Jon Olick - original jo_jpeg.cpp code
* Daniel Gibson - integrate JPEG, allow external zlib
* Aarni Koskela - allow choosing PNG filter
*
* bugfixes:
* github:Chribba
* Guillaume Chereau
* github:jry2
* github:romigrou
* Sergio Gonzalez
* Jonas Karlsson
* Filip Wasil
* Thatcher Ulrich
* github:poppolopoppo
* Patrick Boettcher
* github:xeekworx
* Cap Petschulat
* Simon Rodriguez
* Ivan Tikhonov
* github:ignotion
* Adam Schackart
*
* LICENSE
*
* Public Domain (www.unlicense.org)
*/
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2023 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "third_party/stb/stb_image_write.h"
#include "dsp/core/core.h"
#include "libc/assert.h"
@ -131,16 +29,32 @@
#include "libc/str/str.h"
#include "third_party/zlib/zlib.h"
asm(".ident\t\"\\n\\n\
stb_image_write (Public Domain)\\n\
Credit: Sean Barrett, et al.\\n\
http://nothings.org/stb\"");
#define STBIW_UCHAR(x) (unsigned char)((x)&0xff)
#define STBIW_REALLOC_SIZED(p, oldsz, newsz) realloc(p, newsz)
#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 {
stbi_write_func *func;
void *context;
unsigned char buffer[64];
int buf_used;
} stbi__write_context;
int stbi__flip_vertically_on_write = 0;
int stbi_write_png_compression_level = 4;
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) {
stbi__flip_vertically_on_write = flag;
@ -168,9 +82,6 @@ static void stbi__end_write_file(stbi__write_context *s) {
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) {
while (*fmt) {
switch (*fmt++) {
@ -190,7 +101,7 @@ static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) {
break;
}
case '4': {
stbiw_uint32 x = va_arg(v, int);
unsigned int x = va_arg(v, int);
unsigned char b[4];
b[0] = STBIW_UCHAR(x);
b[1] = STBIW_UCHAR(x >> 8);
@ -212,17 +123,31 @@ static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) {
va_end(v);
}
static void stbiw__write_flush(stbi__write_context *s) {
if (s->buf_used) {
s->func(s->context, &s->buffer, s->buf_used);
s->buf_used = 0;
}
}
static void stbiw__putc(stbi__write_context *s, unsigned char c) {
s->func(s->context, &c, 1);
}
static void stbiw__write1(stbi__write_context *s, unsigned char a) {
if ((size_t)s->buf_used + 1 > sizeof(s->buffer)) stbiw__write_flush(s);
s->buffer[s->buf_used++] = a;
}
static void stbiw__write3(stbi__write_context *s, unsigned char a,
unsigned char b, unsigned char c) {
unsigned char arr[3];
arr[0] = a;
arr[1] = b;
arr[2] = c;
s->func(s->context, arr, 3);
int n;
if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) stbiw__write_flush(s);
n = s->buf_used;
s->buf_used = n + 3;
s->buffer[n + 0] = a;
s->buffer[n + 1] = b;
s->buffer[n + 2] = c;
}
static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp,
@ -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];
int k;
if (write_alpha < 0) s->func(s->context, &d[comp - 1], 1);
if (write_alpha < 0) stbiw__write1(s, d[comp - 1]);
switch (comp) {
case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as
@ -240,7 +165,7 @@ static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp,
if (expand_mono)
stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp
else
s->func(s->context, d, 1); // monochrome TGA
stbiw__write1(s, d[0]); // monochrome TGA
break;
case 4:
if (!write_alpha) {
@ -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]);
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,
int x, int y, int comp, void *data,
int write_alpha, int scanline_pad,
int expand_mono) {
stbiw_uint32 zero = 0;
unsigned int zero = 0;
int i, j, j_end;
if (y <= 0) return;
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;
stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d);
}
stbiw__write_flush(s);
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,
const void *data) {
int pad = (-x * 3) & 3;
return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *)data, 0, pad,
"11 4 22 4"
"4 44 22 444444",
'B', 'M', 14 + 40 + (x * 3 + pad) * y, 0, 0,
14 + 40, // file header
40, x, y, 1, 24, 0, 0, 0, 0, 0, 0); // bitmap header
if (comp != 4) {
// write RGB bitmap
int pad;
pad = (-x * 3) & 3;
return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *)data, 0, pad,
"11 4 22 4"
"4 44 22 444444",
'B', 'M', 14 + 40 + (x * 3 + pad) * y, 0, 0,
14 + 40, // file header
40, x, y, 1, 24, 0, 0, 0, 0, 0, 0); // bitmap header
} else {
// RGBA bitmaps need a v4 header
// use BI_BITFIELDS mode with 32bpp and alpha mask
// (straight BI_RGB with alpha mask doesn't work in most readers)
return stbiw__outfile(s, -1, -1, x, y, comp, 1, (void *)data, 1, 0,
"11 4 22 4"
"4 44 22 444444 4444 4 444 444 444 444",
'B', 'M', 14 + 108 + x * y * 4, 0, 0,
14 + 108, // file header
108, x, y, 1, 32, 3, 0, 0, 0, 0, 0, 0xff0000, 0xff00,
0xff, 0xff000000u, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0); // bitmap V4 header
}
}
int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y,
int comp, const void *data) {
stbi__write_context s;
stbi__write_context s = {0};
stbi__start_write_callbacks(&s, func, context);
return stbi_write_bmp_core(&s, x, y, comp, data);
}
int stbi_write_bmp(char const *filename, int x, int y, int comp,
const void *data) {
stbi__write_context s;
stbi__write_context s = {0};
if (stbi__start_write_file(&s, filename)) {
int r = stbi_write_bmp_core(&s, x, y, comp, data);
stbi__end_write_file(&s);
@ -393,31 +335,32 @@ static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp,
if (diff) {
unsigned char header = STBIW_UCHAR(len - 1);
s->func(s->context, &header, 1);
stbiw__write1(s, header);
for (k = 0; k < len; ++k) {
stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp);
}
} else {
unsigned char header = STBIW_UCHAR(len - 129);
s->func(s->context, &header, 1);
stbiw__write1(s, header);
stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin);
}
}
}
stbiw__write_flush(s);
}
return 1;
}
int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y,
int comp, const void *data) {
stbi__write_context s;
stbi__write_context s = {0};
stbi__start_write_callbacks(&s, func, context);
return stbi_write_tga_core(&s, x, y, comp, (void *)data);
}
int stbi_write_tga(char const *filename, int x, int y, int comp,
const void *data) {
stbi__write_context s;
stbi__write_context s = {0};
if (stbi__start_write_file(&s, filename)) {
int r = stbi_write_tga_core(&s, x, y, comp, (void *)data);
stbi__end_write_file(&s);
@ -426,6 +369,250 @@ int stbi_write_tga(char const *filename, int x, int y, int comp,
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
*
* 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,
int *bitCnt, float *CDU, float *fdtbl, int DC,
int *bitCnt, float *CDU, unsigned du_stride,
float *fdtbl, int DC,
const unsigned short HTDC[256][2],
const unsigned short HTAC[256][2]) {
const unsigned short EOB[2] = {HTAC[0x00][0], HTAC[0x00][1]};
const unsigned short M16zeroes[2] = {HTAC[0xF0][0], HTAC[0xF0][1]};
unsigned i, diff, end0pos;
unsigned i, j, diff, end0pos, x, y;
int DU[64];
dctjpeg((void *)CDU);
dctjpeg((void *)CDU, du_stride / 8);
// Quantize/descale/zigzag the coefficients
for (i = 0; i < 64; ++i) {
float v = CDU[i] * fdtbl[i];
DU[stbiw__jpg_ZigZag[i]] = v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f);
// DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v +
// 0.5f)); ceilf() and floorf() are C99, not C89, but I /think/ they're not
// needed here anyway?
/* DU[stbiw__jpg_ZigZag[i]] = (int)(v < 0 ? v - 0.5f : v + 0.5f); */
for (j = 0, y = 0; y < 8; ++y) {
for (x = 0; x < 8; ++x, ++j) {
float v;
i = y * du_stride + x;
v = CDU[i] * fdtbl[j];
DU[stbiw__jpg_ZigZag[j]] = v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f);
}
}
// Encode DC
@ -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,
0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f};
int row, col, i, k;
int row, col, i, k, subsample;
float fdtbl_Y[64], fdtbl_UV[64];
unsigned char YTable[64], UVTable[64];
@ -718,6 +906,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height,
}
quality = quality ? quality : 97;
subsample = quality <= 97 ? 1 : 0;
quality = quality < 1 ? 1 : quality > 100 ? 100 : quality;
quality = quality < 50 ? 5000 / quality : 200 - quality * 2;
@ -758,7 +947,7 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height,
STBIW_UCHAR(width),
3,
1,
0x11,
(unsigned char)(subsample ? 0x22 : 0x11),
0,
2,
0x11,
@ -802,42 +991,92 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height,
// Encode 8x8 macroblocks
{
static const unsigned short fillBits[] = {0x7F, 7};
const unsigned char *imageData = (const unsigned char *)data;
int DCY = 0, DCU = 0, DCV = 0;
int bitBuf = 0, bitCnt = 0;
// comp == 2 is grey+alpha (alpha is ignored)
int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0;
const unsigned char *dataR = (const unsigned char *)data;
const unsigned char *dataG = dataR + ofsG;
const unsigned char *dataB = dataR + ofsB;
int x, y, pos;
for (y = 0; y < height; y += 8) {
for (x = 0; x < width; x += 8) {
float YDU[64], UDU[64], VDU[64];
for (row = y, pos = 0; row < y + 8; ++row) {
// row >= height => use last input row
int clamped_row = (row < height) ? row : height - 1;
int base_p =
(stbi__flip_vertically_on_write ? (height - 1 - clamped_row)
: clamped_row) *
width * comp;
for (col = x; col < x + 8; ++col, ++pos) {
float r, g, b;
// if col >= width => use pixel from last input column
int p = base_p + ((col < width) ? col : (width - 1)) * comp;
if (subsample) {
for (y = 0; y < height; y += 16) {
for (x = 0; x < width; x += 16) {
float Y[256], U[256], V[256];
for (row = y, pos = 0; row < y + 16; ++row) {
// row >= height => use last input row
int clamped_row = (row < height) ? row : height - 1;
int base_p =
(stbi__flip_vertically_on_write ? (height - 1 - clamped_row)
: clamped_row) *
width * comp;
for (col = x; col < x + 16; ++col, ++pos) {
// if col >= width => use pixel from last input column
int p = base_p + ((col < width) ? col : (width - 1)) * comp;
float r = dataR[p], g = dataG[p], b = dataB[p];
Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128;
U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b;
V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b;
}
}
r = imageData[p + 0];
g = imageData[p + ofsG];
b = imageData[p + ofsB];
YDU[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128;
UDU[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b;
VDU[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b;
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 0, 16, fdtbl_Y,
DCY, YDC_HT, YAC_HT);
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 8, 16, fdtbl_Y,
DCY, YDC_HT, YAC_HT);
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 128, 16, fdtbl_Y,
DCY, YDC_HT, YAC_HT);
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 136, 16, fdtbl_Y,
DCY, YDC_HT, YAC_HT);
// subsample U,V
{
float subU[64], subV[64];
int yy, xx;
for (yy = 0, pos = 0; yy < 8; ++yy) {
for (xx = 0; xx < 8; ++xx, ++pos) {
int j = yy * 32 + xx * 2;
subU[pos] =
(U[j + 0] + U[j + 1] + U[j + 16] + U[j + 17]) * 0.25f;
subV[pos] =
(V[j + 0] + V[j + 1] + V[j + 16] + V[j + 17]) * 0.25f;
}
}
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV,
DCU, UVDC_HT, UVAC_HT);
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV,
DCV, UVDC_HT, UVAC_HT);
}
}
}
} else {
for (y = 0; y < height; y += 8) {
for (x = 0; x < width; x += 8) {
float Y[64], U[64], V[64];
for (row = y, pos = 0; row < y + 8; ++row) {
// row >= height => use last input row
int clamped_row = (row < height) ? row : height - 1;
int base_p =
(stbi__flip_vertically_on_write ? (height - 1 - clamped_row)
: clamped_row) *
width * comp;
for (col = x; col < x + 8; ++col, ++pos) {
// if col >= width => use pixel from last input column
int p = base_p + ((col < width) ? col : (width - 1)) * comp;
float r = dataR[p], g = dataG[p], b = dataB[p];
Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128;
U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b;
V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b;
}
}
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY,
YDC_HT, YAC_HT);
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU,
UVDC_HT, UVAC_HT);
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV,
UVDC_HT, UVAC_HT);
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY,
YDC_HT, YAC_HT);
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU,
UVDC_HT, UVAC_HT);
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV,
UVDC_HT, UVAC_HT);
}
}
}
@ -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 comp, const void *data, int quality) {
stbi__write_context s;
stbi__write_context s = {0};
stbi__start_write_callbacks(&s, func, context);
return stbi_write_jpg_core(&s, x, y, comp, (void *)data, quality);
}
int stbi_write_jpg(char const *filename, int x, int y, int comp,
const void *data, int quality) {
stbi__write_context s;
stbi__write_context s = {0};
if (stbi__start_write_file(&s, filename)) {
int r = stbi_write_jpg_core(&s, x, y, comp, data, quality);
stbi__end_write_file(&s);
@ -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 comp, const float *data) {
stbi__write_context s;
stbi__write_context s = {0};
stbi__start_write_callbacks(&s, func, context);
return stbi_write_hdr_core(&s, x, y, comp, (float *)data);
}
int stbi_write_hdr(char const *filename, int x, int y, int comp,
const float *data) {
stbi__write_context s;
stbi__write_context s = {0};
if (stbi__start_write_file(&s, filename)) {
int r = stbi_write_hdr_core(&s, x, y, comp, (float *)data);
stbi__end_write_file(&s);

View file

@ -3,7 +3,6 @@
COSMOPOLITAN_C_START_
extern int stbi_write_png_compression_level;
extern int stbi__flip_vertically_on_write;
extern int stbi_write_tga_with_rle;
extern int stbi_write_force_png_filter;

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

View file

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

View file

@ -32,6 +32,7 @@
// manxorist@github saga musix github:infatum
// Timur Gagiev Maxwell Koo
//
#include "third_party/stb/stb_vorbis.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
@ -45,6 +46,11 @@
#include "libc/mem/mem.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
// does not compile the code for the various stb_vorbis_*_pushdata()
// functions
@ -343,6 +349,10 @@ struct stb_vorbis {
unsigned int temp_memory_required;
unsigned int setup_temp_memory_required;
char *vendor;
int comment_list_length;
char **comment_list;
// input config
#ifndef STB_VORBIS_NO_STDIO
FILE *f;
@ -358,8 +368,11 @@ struct stb_vorbis {
uint8 push_mode;
// the page to seek to when seeking to start, may be zero
uint32 first_audio_page_offset;
// p_first is the page on which the first audio packet ends
// (but not necessarily the page on which it starts)
ProbedPage p_first, p_last;
// memory management
@ -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) {
sz = (sz + 3) & ~3;
sz = (sz + 7) & ~7; // round up to nearest 8 for alignment of future allocs.
f->setup_memory_required += sz;
if (f->alloc.alloc_buffer) {
void *p = (char *)f->alloc.alloc_buffer + f->setup_offset;
@ -510,7 +523,7 @@ static dontinline void setup_free(vorb *f, void *p) {
}
static dontinline void *setup_temp_malloc(vorb *f, int sz) {
sz = (sz + 3) & ~3;
sz = (sz + 7) & ~7; // round up to nearest 8 for alignment of future allocs.
if (f->alloc.alloc_buffer) {
if (f->temp_offset - sz < f->setup_offset) return NULL;
f->temp_offset -= sz;
@ -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) {
if (f->alloc.alloc_buffer) {
f->temp_offset += (sz + 3) & ~3;
f->temp_offset += (sz + 7) & ~7;
return;
}
free(p);
@ -593,7 +606,7 @@ static float float32_unpack(uint32 x) {
uint32 sign = x & 0x80000000;
uint32 exp = (x & 0x7fe00000) >> 21;
double res = sign ? -(double)mantissa : (double)mantissa;
return (float)ldexp((float)res, exp - 788);
return (float)ldexp((float)res, (int)exp - 788);
}
// zlib & jpeg huffman tables assume that the output symbols
@ -636,6 +649,8 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) {
assert(c->sorted_entries == 0);
return TRUE;
}
// no error return required, code reading lens checks this
assert(len[k] < 32);
// add to the list
add_entry(c, 0, k, m++, len[k], values);
// add all available leaves
@ -648,6 +663,8 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) {
uint32 res;
int z = len[i], y;
if (z == NO_CODE) continue;
// no error return required, code reading lens checks this
assert(z < 32);
// find lowest available leaf (should always be earliest,
// which is what the specification calls for)
// note that this property, and the fact we can never have
@ -659,12 +676,10 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) {
return FALSE;
}
res = available[z];
assert(z >= 0 && z < 32);
available[z] = 0;
add_entry(c, ReverseBits32(res), i, m++, len[i], values);
// propagate availability up the tree
if (z != len[i]) {
assert(len[i] >= 0 && len[i] < 32);
for (y = len[i]; y > z; --y) {
assert(available[y] == 0);
available[y] = res + (1 << (32 - y));
@ -991,6 +1006,9 @@ static int capture_pattern(vorb *f) {
static int start_page_no_capturepattern(vorb *f) {
uint32 loc0, loc1, n;
if (f->first_decode && !IS_PUSH_MODE(f)) {
f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4;
}
// stream structure version
if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version);
// header flag
@ -1027,14 +1045,12 @@ static int start_page_no_capturepattern(vorb *f) {
}
if (f->first_decode) {
int i, len;
ProbedPage p;
len = 0;
for (i = 0; i < f->segment_count; ++i) len += f->segments[i];
len += 27 + f->segment_count;
p.page_start = f->first_audio_page_offset;
p.page_end = p.page_start + len;
p.last_decoded_sample = loc0;
f->p_first = p;
f->p_first.page_end = f->p_first.page_start + len;
f->p_first.last_decoded_sample = loc0;
}
f->next_seg = 0;
return TRUE;
@ -1124,6 +1140,15 @@ static int get8_packet(vorb *f) {
return x;
}
static int get32_packet(vorb *f) {
uint32 x;
x = get8_packet(f);
x += (uint32)get8_packet(f) << 8;
x += (uint32)get8_packet(f) << 16;
x += (uint32)get8_packet(f) << 24;
return x;
}
static void flush_packet(vorb *f) {
while (get8_packet_raw(f) != EOP)
;
@ -1153,7 +1178,7 @@ static uint32 get_bits(vorb *f, int n) {
f->valid_bits += 8;
}
}
if (f->valid_bits < 0) return 0;
assert(f->valid_bits >= n);
z = f->acc & ((1 << n) - 1);
f->acc >>= n;
f->valid_bits -= n;
@ -1225,7 +1250,7 @@ static int codebook_decode_scalar_raw(vorb *f, Codebook *c) {
assert(!c->sparse);
for (i = 0; i < c->entries; ++i) {
if (c->codeword_lengths[i] == NO_CODE) continue;
if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i]) - 1))) {
if (c->codewords[i] == (f->acc & ((1u << c->codeword_lengths[i]) - 1))) {
if (f->valid_bits >= c->codeword_lengths[i]) {
f->acc >>= c->codeword_lengths[i];
f->valid_bits -= c->codeword_lengths[i];
@ -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),
// and the length we'll be using (effective)
if (c_inter + p_inter * ch + effective > len * ch) {
effective = len * ch - (p_inter * ch - c_inter);
// https://github.com/nothings/stb/pull/1490
effective = len * ch - (p_inter * ch + c_inter);
}
#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK
@ -1717,49 +1743,7 @@ static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n,
++class_set;
#endif
}
} else if (ch == 1) {
while (pcount < part_read) {
int z = r->begin + pcount * r->part_size;
int c_inter = 0, p_inter = z;
if (pass_ == 0) {
Codebook *c = f->codebooks + r->classbook;
int q;
DECODE(q, f, c);
if (q == EOP) goto done;
#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
part_classdata[0][class_set] = r->classdata[q];
#else
for (i = classwords - 1; i >= 0; --i) {
classifications[0][i + pcount] = q % r->classifications;
q /= r->classifications;
}
#endif
}
for (i = 0; i < classwords && pcount < part_read; ++i, ++pcount) {
int z = r->begin + pcount * r->part_size;
#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
int c = part_classdata[0][class_set][i];
#else
int c = classifications[0][pcount];
#endif
int b = r->residue_books[c][pass_];
if (b >= 0) {
Codebook *book = f->codebooks + b;
if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers,
ch, &c_inter, &p_inter,
n, r->part_size))
goto done;
} else {
z += r->part_size;
c_inter = 0;
p_inter = z;
}
}
#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE
++class_set;
#endif
}
} else {
} else if (ch > 2) {
while (pcount < part_read) {
int z = r->begin + pcount * r->part_size;
int c_inter = z % ch, p_inter = z / ch;
@ -2165,34 +2149,33 @@ static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A,
while (z > base) {
float k00, k11;
float l00, l11;
k00 = z[-0] - z[-8];
k11 = z[-1] - z[-9];
l00 = z[-2] - z[-10];
l11 = z[-3] - z[-11];
z[-0] = z[-0] + z[-8];
z[-1] = z[-1] + z[-9];
z[-8] = k00;
z[-9] = k11;
k00 = z[-2] - z[-10];
k11 = z[-3] - z[-11];
z[-2] = z[-2] + z[-10];
z[-3] = z[-3] + z[-11];
z[-10] = (k00 + k11) * A2;
z[-11] = (k11 - k00) * A2;
z[-8] = k00;
z[-9] = k11;
z[-10] = (l00 + l11) * A2;
z[-11] = (l11 - l00) * A2;
k00 = z[-12] - z[-4]; // reverse to avoid a unary negation
k00 = z[-4] - z[-12];
k11 = z[-5] - z[-13];
l00 = z[-6] - z[-14];
l11 = z[-7] - z[-15];
z[-4] = z[-4] + z[-12];
z[-5] = z[-5] + z[-13];
z[-12] = k11;
z[-13] = k00;
k00 = z[-14] - z[-6]; // reverse to avoid a unary negation
k11 = z[-7] - z[-15];
z[-6] = z[-6] + z[-14];
z[-7] = z[-7] + z[-15];
z[-14] = (k00 + k11) * A2;
z[-15] = (k00 - k11) * A2;
z[-12] = k11;
z[-13] = -k00;
z[-14] = (l11 - l00) * A2;
z[-15] = (l00 + l11) * -A2;
iter_54(z);
iter_54(z - 8);
@ -2630,7 +2613,8 @@ void inverse_mdct_naive(float *buffer, int n)
#endif
static float *get_window(vorb *f, int len) {
len <<= 1;
// https://github.com/nothings/stb/pull/1499
len = (unsigned int)len << 1;
if (len == f->blocksize_0) return f->window[0];
if (len == f->blocksize_1) return f->window[1];
return NULL;
@ -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) {
Mapping *map;
int i, j, k, n, n2;
int zero_channel[256];
int really_zero_channel[256];
int zero_channel[256] = {0};
int really_zero_channel[256] = {0};
// WINDOWING
@ -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
// and decode the size of all current frames--could be done,
// but presumably it's not a commonly used feature
f->current_loc = -n2; // start of first frame is positioned for discard
f->current_loc = 0u - n2; // start of first frame is positioned for discard
// (NB this is an intentional unsigned
// overflow wrap-around)
// we might have to discard samples "from" the next frame too,
// if we're lapping a large block then a small at the start?
f->discard_samples_deferred = n - right_end;
@ -3089,7 +3075,7 @@ static int vorbis_pump_first_frame(stb_vorbis *f) {
}
#ifndef STB_VORBIS_NO_PUSHDATA_API
static int is_whole_packet_present(stb_vorbis *f, int end_page) {
static int is_whole_packet_present(stb_vorbis *f) {
// make sure that we have the packet available before continuing...
// this requires a full ogg parse, but we know we can fetch from f->stream
@ -3109,8 +3095,6 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page) {
break;
}
// either this continues, or it ends it...
if (end_page)
if (s < f->segment_count - 1) return error(f, VORBIS_invalid_stream);
if (s == f->segment_count) s = -1; // set 'crosses page' flag
if (p > f->stream_end) return error(f, VORBIS_need_more_data);
first = FALSE;
@ -3144,8 +3128,6 @@ static int is_whole_packet_present(stb_vorbis *f, int end_page) {
p += q[s];
if (q[s] < 255) break;
}
if (end_page)
if (s < n - 1) return error(f, VORBIS_invalid_stream);
if (s == n) s = -1; // set 'crosses page' flag
if (p > f->stream_end) return error(f, VORBIS_need_more_data);
first = FALSE;
@ -3160,6 +3142,7 @@ static int start_decoder(vorb *f) {
int longest_floorlist = 0;
// first page, first packet
f->first_decode = TRUE;
if (!start_page(f)) return FALSE;
// validate page flag
@ -3218,6 +3201,50 @@ static int start_decoder(vorb *f) {
if (!start_page(f)) return FALSE;
if (!start_packet(f)) return FALSE;
if (!next_segment(f)) return FALSE;
if (get8_packet(f) != VORBIS_packet_comment)
return error(f, VORBIS_invalid_setup);
for (i = 0; i < 6; ++i) header[i] = get8_packet(f);
if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup);
// file vendor
len = get32_packet(f);
f->vendor = (char *)setup_malloc(f, sizeof(char) * (len + 1));
if (f->vendor == NULL) return error(f, VORBIS_outofmem);
for (i = 0; i < len; ++i) {
f->vendor[i] = get8_packet(f);
}
f->vendor[len] = (char)'\0';
// user comments
f->comment_list_length = get32_packet(f);
f->comment_list = NULL;
if (f->comment_list_length > 0) {
f->comment_list =
(char **)setup_malloc(f, sizeof(char *) * (f->comment_list_length));
if (f->comment_list == NULL) return error(f, VORBIS_outofmem);
}
for (i = 0; i < f->comment_list_length; ++i) {
len = get32_packet(f);
f->comment_list[i] = (char *)setup_malloc(f, sizeof(char) * (len + 1));
if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem);
for (j = 0; j < len; ++j) {
f->comment_list[i][j] = get8_packet(f);
}
f->comment_list[i][len] = (char)'\0';
}
// framing_flag
x = get8_packet(f);
if (!(x & 1)) return error(f, VORBIS_invalid_setup);
skip(f, f->bytes_in_seg);
f->bytes_in_seg = 0;
do {
len = next_segment(f);
skip(f, len);
@ -3229,7 +3256,7 @@ static int start_decoder(vorb *f) {
#ifndef STB_VORBIS_NO_PUSHDATA_API
if (IS_PUSH_MODE(f)) {
if (!is_whole_packet_present(f, TRUE)) {
if (!is_whole_packet_present(f)) {
// convert error in ogg header to write type
if (f->error == VORBIS_invalid_stream) f->error = VORBIS_invalid_setup;
return FALSE;
@ -3302,7 +3329,10 @@ static int start_decoder(vorb *f) {
if (present) {
lengths[j] = get_bits(f, 5) + 1;
++total;
if (lengths[j] == 32) return error(f, VORBIS_invalid_setup);
if (lengths[j] == 32) {
if (c->sparse) setup_temp_free(f, lengths, c->entries);
return error(f, VORBIS_invalid_setup);
}
} else {
lengths[j] = NO_CODE;
}
@ -3315,7 +3345,10 @@ static int start_decoder(vorb *f) {
f->setup_temp_memory_required = c->entries;
c->codeword_lengths = (uint8 *)setup_malloc(f, c->entries);
if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem);
if (c->codeword_lengths == NULL) {
setup_temp_free(f, lengths, c->entries);
return error(f, VORBIS_outofmem);
}
memcpy(c->codeword_lengths, lengths, c->entries);
setup_temp_free(f, lengths,
c->entries); // note this is only safe if there have been
@ -3349,13 +3382,22 @@ static int start_decoder(vorb *f) {
unsigned int size;
if (c->sorted_entries) {
c->codeword_lengths = (uint8 *)setup_malloc(f, c->sorted_entries);
if (!c->codeword_lengths) return error(f, VORBIS_outofmem);
if (!c->codeword_lengths) {
setup_temp_free(f, lengths, c->entries);
return error(f, VORBIS_outofmem);
}
c->codewords = (uint32 *)setup_temp_malloc(
f, sizeof(*c->codewords) * c->sorted_entries);
if (!c->codewords) return error(f, VORBIS_outofmem);
if (!c->codewords) {
setup_temp_free(f, lengths, c->entries);
return error(f, VORBIS_outofmem);
}
values =
(uint32 *)setup_temp_malloc(f, sizeof(*values) * c->sorted_entries);
if (!values) return error(f, VORBIS_outofmem);
if (!values) {
setup_temp_free(f, lengths, c->entries);
return error(f, VORBIS_outofmem);
}
}
size = c->entries +
(sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries;
@ -3364,7 +3406,10 @@ static int start_decoder(vorb *f) {
}
if (!compute_codewords(c, lengths, c->entries, values)) {
if (c->sparse) setup_temp_free(f, values, 0);
if (c->sparse) {
setup_temp_free(f, values, 0);
setup_temp_free(f, lengths, c->entries);
}
return error(f, VORBIS_invalid_setup);
}
@ -3372,12 +3417,18 @@ static int start_decoder(vorb *f) {
// allocate an extra slot for sentinels
c->sorted_codewords = (uint32 *)setup_malloc(
f, sizeof(*c->sorted_codewords) * (c->sorted_entries + 1));
if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem);
if (c->sorted_codewords == NULL) {
if (c->sparse) setup_temp_free(f, lengths, c->entries);
return error(f, VORBIS_outofmem);
}
// allocate an extra slot at the front so that c->sorted_values[-1] is
// defined so that we can catch that case without an extra if
c->sorted_values = (int *)setup_malloc(
f, sizeof(*c->sorted_values) * (c->sorted_entries + 1));
if (c->sorted_values == NULL) return error(f, VORBIS_outofmem);
if (c->sorted_values == NULL) {
if (c->sparse) setup_temp_free(f, lengths, c->entries);
return error(f, VORBIS_outofmem);
}
++c->sorted_values;
c->sorted_values[-1] = -1;
compute_sorted_huffman(c, lengths, values);
@ -3446,8 +3497,7 @@ static int start_decoder(vorb *f) {
unsigned int div = 1;
for (k = 0; k < c->dimensions; ++k) {
int off = (z / div) % c->lookup_values;
float val = mults[off];
val = mults[off] * c->delta_value + c->minimum_value + last;
float val = mults[off] * c->delta_value + c->minimum_value + last;
c->multiplicands[j * c->dimensions + k] = val;
if (c->sequence_p) last = val;
if (k + 1 < c->dimensions) {
@ -3532,7 +3582,7 @@ static int start_decoder(vorb *f) {
return error(f, VORBIS_invalid_setup);
}
for (k = 0; k < 1 << g->class_subclasses[j]; ++k) {
g->subclass_books[j][k] = get_bits(f, 8) - 1;
g->subclass_books[j][k] = (int16)get_bits(f, 8) - 1;
if (g->subclass_books[j][k] >= f->codebook_count)
return error(f, VORBIS_invalid_setup);
}
@ -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;
// precompute the neighbors
for (j = 2; j < g->values; ++j) {
int low, hi;
int low = 0, hi = 0;
neighbors(g->Xlist, j, &low, &hi);
g->neighbors[j][0] = low;
g->neighbors[j][1] = hi;
@ -3738,7 +3788,9 @@ static int start_decoder(vorb *f) {
int i, max_part_read = 0;
for (i = 0; i < f->residue_count; ++i) {
Residue *r = f->residue_config + i;
unsigned int actual_size = f->blocksize_1 / 2;
unsigned int rtype = f->residue_types[i];
unsigned int actual_size =
rtype == 2 ? f->blocksize_1 : f->blocksize_1 / 2;
unsigned int limit_r_begin =
r->begin < actual_size ? r->begin : actual_size;
unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size;
@ -3761,8 +3813,6 @@ static int start_decoder(vorb *f) {
f->temp_memory_required = imdct_mem;
}
f->first_decode = TRUE;
if (f->alloc.alloc_buffer) {
assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes);
// check if there's enough temp memory so we don't error later
@ -3771,13 +3821,30 @@ static int start_decoder(vorb *f) {
return error(f, VORBIS_outofmem);
}
f->first_audio_page_offset = stb_vorbis_get_file_offset(f);
// @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point
// to a page without PAGEFLAG_continued_packet, so this either points
// to the first page, or the page after the end of the headers. It might
// be cleaner to point to a page in the middle of the headers, when that's
// the page where the first audio packet starts, but we'd have to also
// correctly skip the end of any continued packet in stb_vorbis_seek_start.
if (f->next_seg == -1) {
f->first_audio_page_offset = stb_vorbis_get_file_offset(f);
} else {
f->first_audio_page_offset = 0;
}
return TRUE;
}
static void vorbis_deinit(stb_vorbis *p) {
int i, j;
setup_free(p, p->vendor);
for (i = 0; i < p->comment_list_length; ++i) {
setup_free(p, p->comment_list[i]);
}
setup_free(p, p->comment_list);
if (p->residue_config) {
for (i = 0; i < p->residue_count; ++i) {
Residue *r = p->residue_config + i;
@ -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
if (z) {
p->alloc = *z;
p->alloc.alloc_buffer_length_in_bytes =
(p->alloc.alloc_buffer_length_in_bytes + 3) & ~3;
p->alloc.alloc_buffer_length_in_bytes &= ~7;
p->temp_offset = p->alloc.alloc_buffer_length_in_bytes;
}
p->eof = 0;
@ -3873,6 +3939,14 @@ stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) {
return d;
}
stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) {
stb_vorbis_comment d;
d.vendor = f->vendor;
d.comment_list_length = f->comment_list_length;
d.comment_list = f->comment_list;
return d;
}
int stb_vorbis_get_error(stb_vorbis *f) {
int e = f->error;
f->error = VORBIS__no_error;
@ -4007,7 +4081,7 @@ int stb_vorbis_decode_frame_pushdata(
f->error = VORBIS__no_error;
// check that we have the entire packet in memory
if (!is_whole_packet_present(f, FALSE)) {
if (!is_whole_packet_present(f)) {
*samples = 0;
return 0;
}
@ -4069,6 +4143,7 @@ stb_vorbis *stb_vorbis_open_pushdata(
*error = VORBIS_need_more_data;
else
*error = p.error;
vorbis_deinit(&p);
return NULL;
}
f = vorbis_alloc(&p);
@ -4121,7 +4196,7 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) {
if (f->eof) return 0;
if (header[4] != 0) goto invalid;
goal = header[22] + (header[23] << 8) + (header[24] << 16) +
(header[25] << 24);
((uint32)header[25] << 24);
for (i = 22; i < 26; ++i) header[i] = 0;
crc = 0;
for (i = 0; i < 27; ++i) crc = crc32_update(crc, header[i]);
@ -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) {
ProbedPage left, right, mid;
int i, start_seg_with_known_loc, end_pos, page_start;
uint32 delta, stream_length, padding;
double offset, bytes_per_sample;
uint32 delta, stream_length, padding, last_sample_limit;
double offset = 0.0, bytes_per_sample = 0.0;
int probe = 0;
bytes_per_sample = 2; /* TODO(jart): ???? */
@ -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)).
padding = ((f->blocksize_1 - f->blocksize_0) >> 2);
if (sample_number < padding)
sample_number = 0;
last_sample_limit = 0;
else
sample_number -= padding;
last_sample_limit = sample_number - padding;
left = f->p_first;
while (left.last_decoded_sample == ~0U) {
@ -4264,8 +4339,11 @@ static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) {
assert(right.last_decoded_sample != ~0U);
// starting from the start is handled differently
if (sample_number <= left.last_decoded_sample) {
if (stb_vorbis_seek_start(f)) return 1;
if (last_sample_limit <= left.last_decoded_sample) {
if (stb_vorbis_seek_start(f)) {
if (f->current_loc > sample_number) return error(f, VORBIS_seek_failed);
return 1;
}
return 0;
}
@ -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;
offset =
left.page_start +
bytes_per_sample * (sample_number - left.last_decoded_sample);
bytes_per_sample * (last_sample_limit - left.last_decoded_sample);
} else {
// second probe (try to bound the other side)
double error = ((double)sample_number - mid.last_decoded_sample) *
double error = ((double)last_sample_limit - mid.last_decoded_sample) *
bytes_per_sample;
if (error >= 0 && error < 8000) error = 8000;
if (error < 0 && error > -8000) error = -8000;
@ -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,
// and we're close enough.
if (mid.page_start == right.page_start) break;
if (sample_number < mid.last_decoded_sample)
right = mid;
else
left = mid;
// and we're close enough (if it wasn't an interpolation probe).
if (mid.page_start == right.page_start) {
if (probe >= 2 || delta <= 65536) break;
} else {
if (last_sample_limit < mid.last_decoded_sample)
right = mid;
else
left = mid;
}
++probe;
}
@ -4437,8 +4517,8 @@ int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) {
flush_packet(f);
}
}
// the next frame will start with the sample
assert(f->current_loc == sample_number);
// the next frame should start with the sample
if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed);
return 1;
}
@ -4514,7 +4594,8 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) {
// set. whoops!
break;
}
previous_safe = last_page_loc + 1;
// NOTE: not used after this point, but note for debugging
// previous_safe = last_page_loc + 1;
last_page_loc = stb_vorbis_get_file_offset(f);
}
@ -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,
int *error, const stb_vorbis_alloc *alloc) {
stb_vorbis *f, p;
if (data == NULL) return NULL;
if (!data) {
if (error) *error = VORBIS_unexpected_eof;
return NULL;
}
vorbis_init(&p, alloc);
p.stream = (uint8 *)data;
p.stream_end = (uint8 *)data + len;
@ -4684,18 +4768,18 @@ static void copy_samples(short *dest, float *src, int len) {
for (i = 0; i < len; ++i) {
FASTDEF(temp);
int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i], 15);
if ((unsigned int)(v + 32768) > 65535) v = v < 0 ? -32768 : 32767;
if (((unsigned int)v + 32768) > 65535) v = v < 0 ? -32768 : 32767;
dest[i] = v;
}
}
static void compute_samples(int mask, short *output, int num_c, float **data,
int d_offset, int len) {
#define BUFFER_SIZE 32
float buffer[BUFFER_SIZE];
int i, j, o, n = BUFFER_SIZE;
#define STB_BUFFER_SIZE 32
float buffer[STB_BUFFER_SIZE];
int i, j, o, n = STB_BUFFER_SIZE;
check_endianness();
for (o = 0; o < len; o += BUFFER_SIZE) {
for (o = 0; o < len; o += STB_BUFFER_SIZE) {
memset(buffer, 0, sizeof(buffer));
if (o + n > len) n = len - o;
for (j = 0; j < num_c; ++j) {
@ -4706,20 +4790,21 @@ static void compute_samples(int mask, short *output, int num_c, float **data,
for (i = 0; i < n; ++i) {
FASTDEF(temp);
int v = FAST_SCALED_FLOAT_TO_INT(temp, buffer[i], 15);
if ((unsigned int)(v + 32768) > 65535) v = v < 0 ? -32768 : 32767;
if (((unsigned int)v + 32768) > 65535) v = v < 0 ? -32768 : 32767;
output[o + i] = v;
}
}
#undef STB_BUFFER_SIZE
}
static void compute_stereo_samples(short *output, int num_c, float **data,
int d_offset, int len) {
#define BUFFER_SIZE 32
float buffer[BUFFER_SIZE];
int i, j, o, n = BUFFER_SIZE >> 1;
#define STB_BUFFER_SIZE 32
float buffer[STB_BUFFER_SIZE];
int i, j, o, n = STB_BUFFER_SIZE >> 1;
// o is the offset in the source data
check_endianness();
for (o = 0; o < len; o += (BUFFER_SIZE >> 1)) {
for (o = 0; o < len; o += (STB_BUFFER_SIZE >> 1)) {
// o2 is the offset in the output data
int o2 = o << 1;
memset(buffer, 0, sizeof(buffer));
@ -4744,10 +4829,11 @@ static void compute_stereo_samples(short *output, int num_c, float **data,
for (i = 0; i < (n << 1); ++i) {
FASTDEF(temp);
int v = FAST_SCALED_FLOAT_TO_INT(temp, buffer[i], 15);
if ((unsigned int)(v + 32768) > 65535) v = v < 0 ? -32768 : 32767;
if (((unsigned int)v + 32768) > 65535) v = v < 0 ? -32768 : 32767;
output[o2 + i] = v;
}
}
#undef STB_BUFFER_SIZE
}
static void convert_samples_short(int buf_c, short **buffer, int b_offset,
@ -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 num_samples) {
float **output;
float **output = NULL;
int len = stb_vorbis_get_frame_float(f, NULL, &output);
if (len > num_samples) len = num_samples;
if (len) convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len);
@ -4796,7 +4882,7 @@ static void convert_channels_short_interleaved(int buf_c, short *buffer,
float f = data[i][d_offset + j];
int v =
FAST_SCALED_FLOAT_TO_INT(temp, f, 15); // data[i][d_offset+j],15);
if ((unsigned int)(v + 32768) > 65535) v = v < 0 ? -32768 : 32767;
if (((unsigned int)v + 32768) > 65535) v = v < 0 ? -32768 : 32767;
*buffer++ = v;
}
for (; i < buf_c; ++i) *buffer++ = 0;
@ -4824,8 +4910,6 @@ int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels,
float **outputs;
int len = num_shorts / channels;
int n = 0;
int z = f->channels;
if (z > channels) z = channels;
while (n < len) {
int k = f->channel_buffer_end - f->channel_buffer_start;
if (n + k >= len) k = len - n;
@ -4846,8 +4930,6 @@ int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer,
int len) {
float **outputs;
int n = 0;
int z = f->channels;
if (z > channels) z = channels;
while (n < len) {
int k = f->channel_buffer_end - f->channel_buffer_start;
if (n + k >= len) k = len - n;

View file

@ -43,9 +43,18 @@ typedef struct {
int max_frame_size;
} stb_vorbis_info;
typedef struct {
char *vendor;
int comment_list_length;
char **comment_list;
} stb_vorbis_comment;
// get general information about the file
stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f);
// get ogg comments
stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f);
// get the last error detected (clears it, too)
int stb_vorbis_get_error(stb_vorbis *f);
@ -119,6 +128,12 @@ int stb_vorbis_decode_frame_pushdata(
// channel. In other words, (*output)[0][0] contains the first sample from
// the first channel, and (*output)[1][0] contains the first sample from
// the second channel.
//
// *output points into stb_vorbis's internal output buffer storage; these
// buffers are owned by stb_vorbis and application code should not free
// them or modify their contents. They are transient and will be overwritten
// once you ask for more data to get decoded, so be sure to grab any data
// you need before then.
void stb_vorbis_flush_pushdata(stb_vorbis *f);
// inform stb_vorbis that your next datablock will not be contiguous with

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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