mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
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:
parent
7faffde303
commit
1bc48bc8e4
20 changed files with 1560 additions and 1282 deletions
|
@ -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]);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
29
third_party/stb/README.cosmo
vendored
29
third_party/stb/README.cosmo
vendored
|
@ -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
|
||||
|
|
119
third_party/stb/README.txt
vendored
119
third_party/stb/README.txt
vendored
|
@ -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:
|
||||
|
@ -29,34 +28,42 @@
|
|||
* 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
|
||||
* 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,7 +77,8 @@
|
|||
* // ... 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
|
||||
|
@ -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
|
||||
|
|
986
third_party/stb/stb_image.c
vendored
986
third_party/stb/stb_image.c
vendored
File diff suppressed because it is too large
Load diff
15
third_party/stb/stb_image.h
vendored
15
third_party/stb/stb_image.h
vendored
|
@ -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()
|
||||
|
|
30
third_party/stb/stb_image_resize.c
vendored
30
third_party/stb/stb_image_resize.c
vendored
|
@ -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] +=
|
||||
|
|
581
third_party/stb/stb_image_write.c
vendored
581
third_party/stb/stb_image_write.c
vendored
|
@ -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;
|
||||
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,15 +991,68 @@ 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;
|
||||
if (subsample) {
|
||||
for (y = 0; y < height; y += 16) {
|
||||
for (x = 0; x < width; x += 16) {
|
||||
float Y[256], U[256], V[256];
|
||||
for (row = y, pos = 0; row < y + 16; ++row) {
|
||||
// row >= height => use last input row
|
||||
int clamped_row = (row < height) ? row : height - 1;
|
||||
int base_p =
|
||||
(stbi__flip_vertically_on_write ? (height - 1 - clamped_row)
|
||||
: clamped_row) *
|
||||
width * comp;
|
||||
for (col = x; col < x + 16; ++col, ++pos) {
|
||||
// if col >= width => use pixel from last input column
|
||||
int p = base_p + ((col < width) ? col : (width - 1)) * comp;
|
||||
float r = dataR[p], g = dataG[p], b = dataB[p];
|
||||
Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128;
|
||||
U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b;
|
||||
V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b;
|
||||
}
|
||||
}
|
||||
|
||||
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 0, 16, fdtbl_Y,
|
||||
DCY, YDC_HT, YAC_HT);
|
||||
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 8, 16, fdtbl_Y,
|
||||
DCY, YDC_HT, YAC_HT);
|
||||
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 128, 16, fdtbl_Y,
|
||||
DCY, YDC_HT, YAC_HT);
|
||||
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y + 136, 16, fdtbl_Y,
|
||||
DCY, YDC_HT, YAC_HT);
|
||||
|
||||
// subsample U,V
|
||||
{
|
||||
float subU[64], subV[64];
|
||||
int yy, xx;
|
||||
for (yy = 0, pos = 0; yy < 8; ++yy) {
|
||||
for (xx = 0; xx < 8; ++xx, ++pos) {
|
||||
int j = yy * 32 + xx * 2;
|
||||
subU[pos] =
|
||||
(U[j + 0] + U[j + 1] + U[j + 16] + U[j + 17]) * 0.25f;
|
||||
subV[pos] =
|
||||
(V[j + 0] + V[j + 1] + V[j + 16] + V[j + 17]) * 0.25f;
|
||||
}
|
||||
}
|
||||
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV,
|
||||
DCU, UVDC_HT, UVAC_HT);
|
||||
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV,
|
||||
DCV, UVDC_HT, UVAC_HT);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < height; y += 8) {
|
||||
for (x = 0; x < width; x += 8) {
|
||||
float YDU[64], UDU[64], VDU[64];
|
||||
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;
|
||||
|
@ -819,27 +1061,24 @@ static int stbi_write_jpg_core(stbi__write_context *s, int width, int height,
|
|||
: clamped_row) *
|
||||
width * comp;
|
||||
for (col = x; col < x + 8; ++col, ++pos) {
|
||||
float r, g, b;
|
||||
// if col >= width => use pixel from last input column
|
||||
int p = base_p + ((col < width) ? col : (width - 1)) * comp;
|
||||
|
||||
r = imageData[p + 0];
|
||||
g = imageData[p + ofsG];
|
||||
b = imageData[p + ofsB];
|
||||
YDU[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128;
|
||||
UDU[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b;
|
||||
VDU[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b;
|
||||
float r = dataR[p], g = dataG[p], b = dataB[p];
|
||||
Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128;
|
||||
U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b;
|
||||
V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b;
|
||||
}
|
||||
}
|
||||
|
||||
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY,
|
||||
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY,
|
||||
YDC_HT, YAC_HT);
|
||||
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU,
|
||||
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU,
|
||||
UVDC_HT, UVAC_HT);
|
||||
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV,
|
||||
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV,
|
||||
UVDC_HT, UVAC_HT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do the bit alignment of the EOI marker
|
||||
stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits);
|
||||
|
@ -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);
|
||||
|
|
1
third_party/stb/stb_image_write.h
vendored
1
third_party/stb/stb_image_write.h
vendored
|
@ -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;
|
||||
|
||||
|
|
379
third_party/stb/stb_image_write_png.c
vendored
379
third_party/stb/stb_image_write_png.c
vendored
|
@ -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;
|
||||
}
|
45
third_party/stb/stb_rect_pack.c
vendored
45
third_party/stb/stb_rect_pack.c
vendored
|
@ -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)
|
||||
|
|
4
third_party/stb/stb_truetype.c
vendored
4
third_party/stb/stb_truetype.c
vendored
|
@ -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 │
|
||||
|
|
350
third_party/stb/stb_vorbis.c
vendored
350
third_party/stb/stb_vorbis.c
vendored
|
@ -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);
|
||||
}
|
||||
|
||||
// @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)
|
||||
// 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;
|
||||
|
|
15
third_party/stb/stb_vorbis.h
vendored
15
third_party/stb/stb_vorbis.h
vendored
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue