mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
957c61cbbf
This change upgrades to GCC 12.3 and GNU binutils 2.42. The GNU linker appears to have changed things so that only a single de-duplicated str table is present in the binary, and it gets placed wherever the linker wants, regardless of what the linker script says. To cope with that we need to stop using .ident to embed licenses. As such, this change does significant work to revamp how third party licenses are defined in the codebase, using `.section .notice,"aR",@progbits`. This new GCC 12.3 toolchain has support for GNU indirect functions. It lets us support __target_clones__ for the first time. This is used for optimizing the performance of libc string functions such as strlen and friends so far on x86, by ensuring AVX systems favor a second codepath that uses VEX encoding. It shaves some latency off certain operations. It's a useful feature to have for scientific computing for the reasons explained by the test/libcxx/openmp_test.cc example which compiles for fifteen different microarchitectures. Thanks to the upgrades, it's now also possible to use newer instruction sets, such as AVX512FP16, VNNI. Cosmo now uses the %gs register on x86 by default for TLS. Doing it is helpful for any program that links `cosmo_dlopen()`. Such programs had to recompile their binaries at startup to change the TLS instructions. That's not great, since it means every page in the executable needs to be faulted. The work of rewriting TLS-related x86 opcodes, is moved to fixupobj.com instead. This is great news for MacOS x86 users, since we previously needed to morph the binary every time for that platform but now that's no longer necessary. The only platforms where we need fixup of TLS x86 opcodes at runtime are now Windows, OpenBSD, and NetBSD. On Windows we morph TLS to point deeper into the TIB, based on a TlsAlloc assignment, and on OpenBSD/NetBSD we morph %gs back into %fs since the kernels do not allow us to specify a value for the %gs register. OpenBSD users are now required to use APE Loader to run Cosmo binaries and assimilation is no longer possible. OpenBSD kernel needs to change to allow programs to specify a value for the %gs register, or it needs to stop marking executable pages loaded by the kernel as mimmutable(). This release fixes __constructor__, .ctor, .init_array, and lastly the .preinit_array so they behave the exact same way as glibc. We no longer use hex constants to define math.h symbols like M_PI.
1282 lines
48 KiB
C
1282 lines
48 KiB
C
/*-*- 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"
|
|
#include "libc/fmt/conv.h"
|
|
#include "libc/limits.h"
|
|
#include "libc/macros.internal.h"
|
|
#include "libc/math.h"
|
|
#include "libc/mem/mem.h"
|
|
#include "libc/nexgen32e/nexgen32e.h"
|
|
#include "libc/stdio/stdio.h"
|
|
#include "libc/str/str.h"
|
|
#include "third_party/zlib/zlib.h"
|
|
|
|
__notice(stb_image_write_notice, "\
|
|
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__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_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;
|
|
}
|
|
|
|
// initialize a callback-based context
|
|
static void stbi__start_write_callbacks(stbi__write_context *s,
|
|
stbi_write_func *c, void *context) {
|
|
s->func = c;
|
|
s->context = context;
|
|
}
|
|
|
|
static void stbi__stdio_write(void *context, void *data, int size) {
|
|
fwrite(data, 1, size, (FILE *)context);
|
|
}
|
|
|
|
static int stbi__start_write_file(stbi__write_context *s,
|
|
const char *filename) {
|
|
FILE *f = fopen(filename, "wb");
|
|
stbi__start_write_callbacks(s, stbi__stdio_write, (void *)f);
|
|
return f != NULL;
|
|
}
|
|
|
|
static void stbi__end_write_file(stbi__write_context *s) {
|
|
fclose((FILE *)s->context);
|
|
}
|
|
|
|
static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) {
|
|
while (*fmt) {
|
|
switch (*fmt++) {
|
|
case ' ':
|
|
break;
|
|
case '1': {
|
|
unsigned char x = STBIW_UCHAR(va_arg(v, int));
|
|
s->func(s->context, &x, 1);
|
|
break;
|
|
}
|
|
case '2': {
|
|
int x = va_arg(v, int);
|
|
unsigned char b[2];
|
|
b[0] = STBIW_UCHAR(x);
|
|
b[1] = STBIW_UCHAR(x >> 8);
|
|
s->func(s->context, b, 2);
|
|
break;
|
|
}
|
|
case '4': {
|
|
unsigned int x = va_arg(v, int);
|
|
unsigned char b[4];
|
|
b[0] = STBIW_UCHAR(x);
|
|
b[1] = STBIW_UCHAR(x >> 8);
|
|
b[2] = STBIW_UCHAR(x >> 16);
|
|
b[3] = STBIW_UCHAR(x >> 24);
|
|
s->func(s->context, b, 4);
|
|
break;
|
|
}
|
|
default:
|
|
__builtin_unreachable();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) {
|
|
va_list v;
|
|
va_start(v, fmt);
|
|
stbiw__writefv(s, fmt, v);
|
|
va_end(v);
|
|
}
|
|
|
|
static void stbiw__write_flush(stbi__write_context *s) {
|
|
if (s->buf_used) {
|
|
s->func(s->context, &s->buffer, s->buf_used);
|
|
s->buf_used = 0;
|
|
}
|
|
}
|
|
|
|
static void stbiw__putc(stbi__write_context *s, unsigned char c) {
|
|
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) {
|
|
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,
|
|
int write_alpha, int expand_mono,
|
|
unsigned char *d) {
|
|
unsigned char bg[3] = {255, 0, 255}, px[3];
|
|
int k;
|
|
|
|
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
|
|
// 1-channel case
|
|
case 1:
|
|
if (expand_mono)
|
|
stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp
|
|
else
|
|
stbiw__write1(s, d[0]); // monochrome TGA
|
|
break;
|
|
case 4:
|
|
if (!write_alpha) {
|
|
// composite against pink background
|
|
for (k = 0; k < 3; ++k) px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;
|
|
stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]);
|
|
break;
|
|
}
|
|
/* FALLTHROUGH */
|
|
case 3:
|
|
stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]);
|
|
break;
|
|
}
|
|
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) {
|
|
unsigned int zero = 0;
|
|
int i, j, j_end;
|
|
if (y <= 0) return;
|
|
if (stbi__flip_vertically_on_write) vdir *= -1;
|
|
if (vdir < 0) {
|
|
j_end = -1;
|
|
j = y - 1;
|
|
} else {
|
|
j_end = y;
|
|
j = 0;
|
|
}
|
|
for (; j != j_end; j += vdir) {
|
|
for (i = 0; i < x; ++i) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x,
|
|
int y, int comp, int expand_mono, void *data,
|
|
int alpha, int pad, const char *fmt, ...) {
|
|
if (y < 0 || x < 0) {
|
|
return 0;
|
|
} else {
|
|
va_list v;
|
|
va_start(v, fmt);
|
|
stbiw__writefv(s, fmt, v);
|
|
va_end(v);
|
|
stbiw__write_pixels(s, rgb_dir, vdir, x, y, comp, data, alpha, pad,
|
|
expand_mono);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp,
|
|
const void *data) {
|
|
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 = {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 = {0};
|
|
if (stbi__start_write_file(&s, filename)) {
|
|
int r = stbi_write_bmp_core(&s, x, y, comp, data);
|
|
stbi__end_write_file(&s);
|
|
return r;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp,
|
|
void *data) {
|
|
int has_alpha = (comp == 2 || comp == 4);
|
|
int colorbytes = has_alpha ? comp - 1 : comp;
|
|
int format =
|
|
colorbytes < 2
|
|
? 3
|
|
: 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3
|
|
|
|
if (y < 0 || x < 0) return 0;
|
|
|
|
if (!stbi_write_tga_with_rle) {
|
|
return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *)data, has_alpha, 0,
|
|
"111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y,
|
|
(colorbytes + has_alpha) * 8, has_alpha * 8);
|
|
} else {
|
|
int i, j, k;
|
|
int jend, jdir;
|
|
|
|
stbiw__writef(s, "111 221 2222 11", 0, 0, format + 8, 0, 0, 0, 0, 0, x, y,
|
|
(colorbytes + has_alpha) * 8, has_alpha * 8);
|
|
|
|
if (stbi__flip_vertically_on_write) {
|
|
j = 0;
|
|
jend = y;
|
|
jdir = 1;
|
|
} else {
|
|
j = y - 1;
|
|
jend = -1;
|
|
jdir = -1;
|
|
}
|
|
for (; j != jend; j += jdir) {
|
|
unsigned char *row = (unsigned char *)data + j * x * comp;
|
|
int len;
|
|
|
|
for (i = 0; i < x; i += len) {
|
|
unsigned char *begin = row + i * comp;
|
|
int diff = 1;
|
|
len = 1;
|
|
|
|
if (i < x - 1) {
|
|
++len;
|
|
diff = memcmp(begin, row + (i + 1) * comp, comp);
|
|
if (diff) {
|
|
const unsigned char *prev = begin;
|
|
for (k = i + 2; k < x && len < 128; ++k) {
|
|
if (memcmp(prev, row + k * comp, comp)) {
|
|
prev += comp;
|
|
++len;
|
|
} else {
|
|
--len;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for (k = i + 2; k < x && len < 128; ++k) {
|
|
if (!memcmp(begin, row + k * comp, comp)) {
|
|
++len;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (diff) {
|
|
unsigned char header = STBIW_UCHAR(len - 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);
|
|
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 = {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 = {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);
|
|
return r;
|
|
} else
|
|
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:
|
|
* public domain Simple, Minimalistic JPEG writer -
|
|
* http://www.jonolick.com/code.html
|
|
*/
|
|
|
|
static const unsigned char stbiw__jpg_ZigZag[] = {
|
|
0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42,
|
|
3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53,
|
|
10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60,
|
|
21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63,
|
|
};
|
|
|
|
static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP,
|
|
int *bitCntP, const unsigned short *bs) {
|
|
unsigned char c;
|
|
unsigned bitBuf, bitCnt;
|
|
bitBuf = *bitBufP;
|
|
bitCnt = *bitCntP;
|
|
bitCnt += bs[1];
|
|
bitBuf |= bs[0] << (24 - bitCnt);
|
|
while (bitCnt >= 8) {
|
|
c = (bitBuf >> 16) & 255;
|
|
stbiw__putc(s, c);
|
|
if (c == 255) {
|
|
stbiw__putc(s, 0);
|
|
}
|
|
bitBuf <<= 8;
|
|
bitCnt -= 8;
|
|
}
|
|
*bitBufP = bitBuf;
|
|
*bitCntP = bitCnt;
|
|
}
|
|
|
|
static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) {
|
|
int tmp1 = val < 0 ? -val : val;
|
|
val = val < 0 ? val - 1 : val;
|
|
bits[1] = 1;
|
|
while (tmp1 >>= 1) {
|
|
++bits[1];
|
|
}
|
|
bits[0] = val & ((1u << bits[1]) - 1);
|
|
}
|
|
|
|
static int stbiw__jpg_processDU(stbi__write_context *s, int *bitBuf,
|
|
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, j, diff, end0pos, x, y;
|
|
int DU[64];
|
|
|
|
dctjpeg((void *)CDU, du_stride / 8);
|
|
|
|
// Quantize/descale/zigzag the coefficients
|
|
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
|
|
diff = DU[0] - DC;
|
|
if (diff == 0) {
|
|
stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[0]);
|
|
} else {
|
|
unsigned short bits[2];
|
|
stbiw__jpg_calcBits(diff, bits);
|
|
stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTDC[bits[1]]);
|
|
stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);
|
|
}
|
|
// Encode ACs
|
|
end0pos = 63;
|
|
for (; (end0pos > 0) && (DU[end0pos] == 0); --end0pos) {
|
|
}
|
|
// end0pos = first element in reverse order !=0
|
|
if (end0pos == 0) {
|
|
stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
|
|
return DU[0];
|
|
}
|
|
for (i = 1; i <= end0pos; ++i) {
|
|
unsigned startpos = i;
|
|
unsigned nrzeroes;
|
|
unsigned short bits[2];
|
|
for (; DU[i] == 0 && i <= end0pos; ++i) {
|
|
}
|
|
nrzeroes = i - startpos;
|
|
if (nrzeroes >= 16) {
|
|
unsigned lng = nrzeroes >> 4;
|
|
unsigned nrmarker;
|
|
for (nrmarker = 1; nrmarker <= lng; ++nrmarker) {
|
|
stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes);
|
|
}
|
|
nrzeroes &= 15;
|
|
}
|
|
stbiw__jpg_calcBits(DU[i], bits);
|
|
stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes << 4) + bits[1]]);
|
|
stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits);
|
|
}
|
|
if (end0pos != 63) {
|
|
stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB);
|
|
}
|
|
return DU[0];
|
|
}
|
|
|
|
static int stbi_write_jpg_core(stbi__write_context *s, int width, int height,
|
|
int comp, const void *data, int quality) {
|
|
static const unsigned char std_dc_luminance_nrcodes[] = {
|
|
0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0};
|
|
static const unsigned char std_dc_luminance_values[] = {0, 1, 2, 3, 4, 5,
|
|
6, 7, 8, 9, 10, 11};
|
|
static const unsigned char std_ac_luminance_nrcodes[] = {
|
|
0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d};
|
|
static const unsigned char std_ac_luminance_values[] = {
|
|
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
|
|
0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
|
|
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72,
|
|
0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
|
|
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
|
|
0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
|
|
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75,
|
|
0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
|
|
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
|
|
0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
|
|
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
|
|
0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
|
|
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4,
|
|
0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa};
|
|
static const unsigned char std_dc_chrominance_nrcodes[] = {
|
|
0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
|
|
static const unsigned char std_dc_chrominance_values[] = {0, 1, 2, 3, 4, 5,
|
|
6, 7, 8, 9, 10, 11};
|
|
static const unsigned char std_ac_chrominance_nrcodes[] = {
|
|
0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77};
|
|
static const unsigned char std_ac_chrominance_values[] = {
|
|
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41,
|
|
0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
|
|
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
|
|
0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
|
|
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44,
|
|
0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
|
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74,
|
|
0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
|
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
|
|
0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
|
|
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
|
|
0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
|
|
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
|
|
0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa};
|
|
// Huffman tables
|
|
static const unsigned short YDC_HT[256][2] = {
|
|
{0, 2}, {2, 3}, {3, 3}, {4, 3}, {5, 3}, {6, 3},
|
|
{14, 4}, {30, 5}, {62, 6}, {126, 7}, {254, 8}, {510, 9}};
|
|
static const unsigned short UVDC_HT[256][2] = {
|
|
{0, 2}, {1, 2}, {2, 2}, {6, 3}, {14, 4}, {30, 5},
|
|
{62, 6}, {126, 7}, {254, 8}, {510, 9}, {1022, 10}, {2046, 11}};
|
|
static const unsigned short YAC_HT[256][2] = {
|
|
{10, 4}, {0, 2}, {1, 2}, {4, 3}, {11, 4},
|
|
{26, 5}, {120, 7}, {248, 8}, {1014, 10}, {65410, 16},
|
|
{65411, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {12, 4}, {27, 5}, {121, 7},
|
|
{502, 9}, {2038, 11}, {65412, 16}, {65413, 16}, {65414, 16},
|
|
{65415, 16}, {65416, 16}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {28, 5}, {249, 8},
|
|
{1015, 10}, {4084, 12}, {65417, 16}, {65418, 16}, {65419, 16},
|
|
{65420, 16}, {65421, 16}, {65422, 16}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {58, 6},
|
|
{503, 9}, {4085, 12}, {65423, 16}, {65424, 16}, {65425, 16},
|
|
{65426, 16}, {65427, 16}, {65428, 16}, {65429, 16}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{59, 6}, {1016, 10}, {65430, 16}, {65431, 16}, {65432, 16},
|
|
{65433, 16}, {65434, 16}, {65435, 16}, {65436, 16}, {65437, 16},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {122, 7}, {2039, 11}, {65438, 16}, {65439, 16},
|
|
{65440, 16}, {65441, 16}, {65442, 16}, {65443, 16}, {65444, 16},
|
|
{65445, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {123, 7}, {4086, 12}, {65446, 16},
|
|
{65447, 16}, {65448, 16}, {65449, 16}, {65450, 16}, {65451, 16},
|
|
{65452, 16}, {65453, 16}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {250, 8}, {4087, 12},
|
|
{65454, 16}, {65455, 16}, {65456, 16}, {65457, 16}, {65458, 16},
|
|
{65459, 16}, {65460, 16}, {65461, 16}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {504, 9},
|
|
{32704, 15}, {65462, 16}, {65463, 16}, {65464, 16}, {65465, 16},
|
|
{65466, 16}, {65467, 16}, {65468, 16}, {65469, 16}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{505, 9}, {65470, 16}, {65471, 16}, {65472, 16}, {65473, 16},
|
|
{65474, 16}, {65475, 16}, {65476, 16}, {65477, 16}, {65478, 16},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {506, 9}, {65479, 16}, {65480, 16}, {65481, 16},
|
|
{65482, 16}, {65483, 16}, {65484, 16}, {65485, 16}, {65486, 16},
|
|
{65487, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {1017, 10}, {65488, 16}, {65489, 16},
|
|
{65490, 16}, {65491, 16}, {65492, 16}, {65493, 16}, {65494, 16},
|
|
{65495, 16}, {65496, 16}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {1018, 10}, {65497, 16},
|
|
{65498, 16}, {65499, 16}, {65500, 16}, {65501, 16}, {65502, 16},
|
|
{65503, 16}, {65504, 16}, {65505, 16}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {2040, 11},
|
|
{65506, 16}, {65507, 16}, {65508, 16}, {65509, 16}, {65510, 16},
|
|
{65511, 16}, {65512, 16}, {65513, 16}, {65514, 16}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{65515, 16}, {65516, 16}, {65517, 16}, {65518, 16}, {65519, 16},
|
|
{65520, 16}, {65521, 16}, {65522, 16}, {65523, 16}, {65524, 16},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{2041, 11}, {65525, 16}, {65526, 16}, {65527, 16}, {65528, 16},
|
|
{65529, 16}, {65530, 16}, {65531, 16}, {65532, 16}, {65533, 16},
|
|
{65534, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}};
|
|
static const unsigned short UVAC_HT[256][2] = {
|
|
{0, 2}, {1, 2}, {4, 3}, {10, 4}, {24, 5},
|
|
{25, 5}, {56, 6}, {120, 7}, {500, 9}, {1014, 10},
|
|
{4084, 12}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {11, 4}, {57, 6}, {246, 8},
|
|
{501, 9}, {2038, 11}, {4085, 12}, {65416, 16}, {65417, 16},
|
|
{65418, 16}, {65419, 16}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {26, 5}, {247, 8},
|
|
{1015, 10}, {4086, 12}, {32706, 15}, {65420, 16}, {65421, 16},
|
|
{65422, 16}, {65423, 16}, {65424, 16}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {27, 5},
|
|
{248, 8}, {1016, 10}, {4087, 12}, {65425, 16}, {65426, 16},
|
|
{65427, 16}, {65428, 16}, {65429, 16}, {65430, 16}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{58, 6}, {502, 9}, {65431, 16}, {65432, 16}, {65433, 16},
|
|
{65434, 16}, {65435, 16}, {65436, 16}, {65437, 16}, {65438, 16},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {59, 6}, {1017, 10}, {65439, 16}, {65440, 16},
|
|
{65441, 16}, {65442, 16}, {65443, 16}, {65444, 16}, {65445, 16},
|
|
{65446, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {121, 7}, {2039, 11}, {65447, 16},
|
|
{65448, 16}, {65449, 16}, {65450, 16}, {65451, 16}, {65452, 16},
|
|
{65453, 16}, {65454, 16}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {122, 7}, {2040, 11},
|
|
{65455, 16}, {65456, 16}, {65457, 16}, {65458, 16}, {65459, 16},
|
|
{65460, 16}, {65461, 16}, {65462, 16}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {249, 8},
|
|
{65463, 16}, {65464, 16}, {65465, 16}, {65466, 16}, {65467, 16},
|
|
{65468, 16}, {65469, 16}, {65470, 16}, {65471, 16}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{503, 9}, {65472, 16}, {65473, 16}, {65474, 16}, {65475, 16},
|
|
{65476, 16}, {65477, 16}, {65478, 16}, {65479, 16}, {65480, 16},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {504, 9}, {65481, 16}, {65482, 16}, {65483, 16},
|
|
{65484, 16}, {65485, 16}, {65486, 16}, {65487, 16}, {65488, 16},
|
|
{65489, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {505, 9}, {65490, 16}, {65491, 16},
|
|
{65492, 16}, {65493, 16}, {65494, 16}, {65495, 16}, {65496, 16},
|
|
{65497, 16}, {65498, 16}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {506, 9}, {65499, 16},
|
|
{65500, 16}, {65501, 16}, {65502, 16}, {65503, 16}, {65504, 16},
|
|
{65505, 16}, {65506, 16}, {65507, 16}, {0, 0}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {2041, 11},
|
|
{65508, 16}, {65509, 16}, {65510, 16}, {65511, 16}, {65512, 16},
|
|
{65513, 16}, {65514, 16}, {65515, 16}, {65516, 16}, {0, 0},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{16352, 14}, {65517, 16}, {65518, 16}, {65519, 16}, {65520, 16},
|
|
{65521, 16}, {65522, 16}, {65523, 16}, {65524, 16}, {65525, 16},
|
|
{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{1018, 10}, {32707, 15}, {65526, 16}, {65527, 16}, {65528, 16},
|
|
{65529, 16}, {65530, 16}, {65531, 16}, {65532, 16}, {65533, 16},
|
|
{65534, 16}, {0, 0}, {0, 0}, {0, 0}, {0, 0},
|
|
{0, 0}};
|
|
static const int YQT[] = {
|
|
16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55,
|
|
14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62,
|
|
18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92,
|
|
49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99};
|
|
static const int UVQT[] = {17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99,
|
|
99, 99, 99, 24, 26, 56, 99, 99, 99, 99, 99, 47, 66,
|
|
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
|
|
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
|
|
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99};
|
|
static const float aasf[] = {
|
|
1.0f * 2.828427125f, 1.387039845f * 2.828427125f,
|
|
1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f,
|
|
1.0f * 2.828427125f, 0.785694958f * 2.828427125f,
|
|
0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f};
|
|
|
|
int row, col, i, k, subsample;
|
|
float fdtbl_Y[64], fdtbl_UV[64];
|
|
unsigned char YTable[64], UVTable[64];
|
|
|
|
if (!data || !width || !height || comp > 4 || comp < 1) {
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
|
|
for (i = 0; i < 64; ++i) {
|
|
int uvti, yti = (YQT[i] * quality + 50) / 100;
|
|
YTable[stbiw__jpg_ZigZag[i]] = (unsigned char)(yti < 1 ? 1
|
|
: yti > 255 ? 255
|
|
: yti);
|
|
uvti = (UVQT[i] * quality + 50) / 100;
|
|
UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char)(uvti < 1 ? 1
|
|
: uvti > 255 ? 255
|
|
: uvti);
|
|
}
|
|
|
|
for (row = 0, k = 0; row < 8; ++row) {
|
|
for (col = 0; col < 8; ++col, ++k) {
|
|
fdtbl_Y[k] = 1 / (YTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
|
|
fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]);
|
|
}
|
|
}
|
|
|
|
// Write Headers
|
|
{
|
|
static const unsigned char head0[] = {
|
|
0xFF, 0xD8, 0xFF, 0xE0, 0, 0x10, 'J', 'F', 'I', 'F', 0, 1, 1,
|
|
0, 0, 1, 0, 1, 0, 0, 0xFF, 0xDB, 0, 0x84, 0};
|
|
static const unsigned char head2[] = {0xFF, 0xDA, 0, 0xC, 3, 1, 0,
|
|
2, 0x11, 3, 0x11, 0, 0x3F, 0};
|
|
const unsigned char head1[] = {
|
|
0xFF,
|
|
0xC0,
|
|
0,
|
|
0x11,
|
|
8,
|
|
(unsigned char)(height >> 8),
|
|
STBIW_UCHAR(height),
|
|
(unsigned char)(width >> 8),
|
|
STBIW_UCHAR(width),
|
|
3,
|
|
1,
|
|
(unsigned char)(subsample ? 0x22 : 0x11),
|
|
0,
|
|
2,
|
|
0x11,
|
|
1,
|
|
3,
|
|
0x11,
|
|
1,
|
|
0xFF,
|
|
0xC4,
|
|
0x01,
|
|
0xA2,
|
|
0,
|
|
};
|
|
s->func(s->context, (void *)head0, sizeof(head0));
|
|
s->func(s->context, (void *)YTable, sizeof(YTable));
|
|
stbiw__putc(s, 1);
|
|
s->func(s->context, UVTable, sizeof(UVTable));
|
|
s->func(s->context, (void *)head1, sizeof(head1));
|
|
s->func(s->context, (void *)(std_dc_luminance_nrcodes + 1),
|
|
sizeof(std_dc_luminance_nrcodes) - 1);
|
|
s->func(s->context, (void *)std_dc_luminance_values,
|
|
sizeof(std_dc_luminance_values));
|
|
stbiw__putc(s, 0x10); // HTYACinfo
|
|
s->func(s->context, (void *)(std_ac_luminance_nrcodes + 1),
|
|
sizeof(std_ac_luminance_nrcodes) - 1);
|
|
s->func(s->context, (void *)std_ac_luminance_values,
|
|
sizeof(std_ac_luminance_values));
|
|
stbiw__putc(s, 1); // HTUDCinfo
|
|
s->func(s->context, (void *)(std_dc_chrominance_nrcodes + 1),
|
|
sizeof(std_dc_chrominance_nrcodes) - 1);
|
|
s->func(s->context, (void *)std_dc_chrominance_values,
|
|
sizeof(std_dc_chrominance_values));
|
|
stbiw__putc(s, 0x11); // HTUACinfo
|
|
s->func(s->context, (void *)(std_ac_chrominance_nrcodes + 1),
|
|
sizeof(std_ac_chrominance_nrcodes) - 1);
|
|
s->func(s->context, (void *)std_ac_chrominance_values,
|
|
sizeof(std_ac_chrominance_values));
|
|
s->func(s->context, (void *)head2, sizeof(head2));
|
|
}
|
|
|
|
// Encode 8x8 macroblocks
|
|
{
|
|
static const unsigned short fillBits[] = {0x7F, 7};
|
|
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 Y[64], U[64], V[64];
|
|
for (row = y, pos = 0; row < y + 8; ++row) {
|
|
// row >= height => use last input row
|
|
int clamped_row = (row < height) ? row : height - 1;
|
|
int base_p =
|
|
(stbi__flip_vertically_on_write ? (height - 1 - clamped_row)
|
|
: clamped_row) *
|
|
width * comp;
|
|
for (col = x; col < x + 8; ++col, ++pos) {
|
|
// if col >= width => use pixel from last input column
|
|
int p = base_p + ((col < width) ? col : (width - 1)) * comp;
|
|
float r = dataR[p], g = dataG[p], b = dataB[p];
|
|
Y[pos] = +0.29900f * r + 0.58700f * g + 0.11400f * b - 128;
|
|
U[pos] = -0.16874f * r - 0.33126f * g + 0.50000f * b;
|
|
V[pos] = +0.50000f * r - 0.41869f * g - 0.08131f * b;
|
|
}
|
|
}
|
|
|
|
DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY,
|
|
YDC_HT, YAC_HT);
|
|
DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU,
|
|
UVDC_HT, UVAC_HT);
|
|
DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV,
|
|
UVDC_HT, UVAC_HT);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Do the bit alignment of the EOI marker
|
|
stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits);
|
|
}
|
|
|
|
// EOI
|
|
stbiw__putc(s, 0xFF);
|
|
stbiw__putc(s, 0xD9);
|
|
|
|
return 1;
|
|
}
|
|
|
|
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 = {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 = {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);
|
|
return r;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
********************************************************************************
|
|
Radiance RGBE HDR writer
|
|
by Baldur Karlsson */
|
|
|
|
static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) {
|
|
int exponent;
|
|
float normalize, maxcomp;
|
|
maxcomp = MAX(linear[0], MAX(linear[1], linear[2]));
|
|
if (maxcomp < 1e-32f) {
|
|
rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
|
|
} else {
|
|
/* no idea what HDR is but this could probably use clamping */
|
|
normalize = (float)frexp(maxcomp, &exponent) * 256.0f / maxcomp;
|
|
rgbe[0] = (unsigned char)(linear[0] * normalize);
|
|
rgbe[1] = (unsigned char)(linear[1] * normalize);
|
|
rgbe[2] = (unsigned char)(linear[2] * normalize);
|
|
rgbe[3] = (unsigned char)(exponent + 128);
|
|
}
|
|
}
|
|
|
|
static void stbiw__write_run_data(stbi__write_context *s, int length,
|
|
unsigned char databyte) {
|
|
unsigned char lengthbyte = STBIW_UCHAR(length + 128);
|
|
assert(length + 128 <= 255);
|
|
s->func(s->context, &lengthbyte, 1);
|
|
s->func(s->context, &databyte, 1);
|
|
}
|
|
|
|
static void stbiw__write_dump_data(stbi__write_context *s, int length,
|
|
unsigned char *data) {
|
|
unsigned char lengthbyte = STBIW_UCHAR(length);
|
|
assert(length <=
|
|
128); // inconsistent with spec but consistent with official code
|
|
s->func(s->context, &lengthbyte, 1);
|
|
s->func(s->context, data, length);
|
|
}
|
|
|
|
static void stbiw__write_hdr_scanline(stbi__write_context *s, int width,
|
|
int ncomp, unsigned char *scratch,
|
|
float *scanline) {
|
|
unsigned char scanlineheader[4] = {2, 2, 0, 0};
|
|
unsigned char rgbe[4];
|
|
float linear[3];
|
|
int x;
|
|
|
|
scanlineheader[2] = (width & 0xff00) >> 8;
|
|
scanlineheader[3] = (width & 0x00ff);
|
|
|
|
/* skip RLE for images too small or large */
|
|
if (width < 8 || width >= 32768) {
|
|
for (x = 0; x < width; x++) {
|
|
switch (ncomp) {
|
|
case 4: /* fallthrough */
|
|
case 3:
|
|
linear[2] = scanline[x * ncomp + 2];
|
|
linear[1] = scanline[x * ncomp + 1];
|
|
linear[0] = scanline[x * ncomp + 0];
|
|
break;
|
|
default:
|
|
linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0];
|
|
break;
|
|
}
|
|
stbiw__linear_to_rgbe(rgbe, linear);
|
|
s->func(s->context, rgbe, 4);
|
|
}
|
|
} else {
|
|
int c, r;
|
|
/* encode into scratch buffer */
|
|
for (x = 0; x < width; x++) {
|
|
switch (ncomp) {
|
|
case 4: /* fallthrough */
|
|
case 3:
|
|
linear[2] = scanline[x * ncomp + 2];
|
|
linear[1] = scanline[x * ncomp + 1];
|
|
linear[0] = scanline[x * ncomp + 0];
|
|
break;
|
|
default:
|
|
linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0];
|
|
break;
|
|
}
|
|
stbiw__linear_to_rgbe(rgbe, linear);
|
|
scratch[x + width * 0] = rgbe[0];
|
|
scratch[x + width * 1] = rgbe[1];
|
|
scratch[x + width * 2] = rgbe[2];
|
|
scratch[x + width * 3] = rgbe[3];
|
|
}
|
|
|
|
s->func(s->context, scanlineheader, 4);
|
|
|
|
/* RLE each component separately */
|
|
for (c = 0; c < 4; c++) {
|
|
unsigned char *comp = &scratch[width * c];
|
|
|
|
x = 0;
|
|
while (x < width) {
|
|
// find first run
|
|
r = x;
|
|
while (r + 2 < width) {
|
|
if (comp[r] == comp[r + 1] && comp[r] == comp[r + 2]) break;
|
|
++r;
|
|
}
|
|
if (r + 2 >= width) r = width;
|
|
// dump up to first run
|
|
while (x < r) {
|
|
int len = r - x;
|
|
if (len > 128) len = 128;
|
|
stbiw__write_dump_data(s, len, &comp[x]);
|
|
x += len;
|
|
}
|
|
// if there's a run, output it
|
|
if (r + 2 < width) { // same test as what we break out of in search
|
|
// loop, so only true if we break'd
|
|
// find next byte after run
|
|
while (r < width && comp[r] == comp[x]) ++r;
|
|
// output run up to r
|
|
while (x < r) {
|
|
int len = r - x;
|
|
if (len > 127) len = 127;
|
|
stbiw__write_run_data(s, len, comp[x]);
|
|
x += len;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp,
|
|
float *data) {
|
|
if (y <= 0 || x <= 0 || data == NULL)
|
|
return 0;
|
|
else {
|
|
// Each component is stored separately. Allocate scratch space for full
|
|
// output scanline.
|
|
unsigned char *scratch = malloc(x * 4);
|
|
int i, len;
|
|
char buffer[128];
|
|
char header[] =
|
|
"#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n";
|
|
s->func(s->context, header, sizeof(header) - 1);
|
|
len = (snprintf)(buffer, sizeof(buffer), "%s\n\n%s%d%s%d\n",
|
|
"EXPOSURE= 1.0000000000000", "-Y ", y, " +X ", x);
|
|
s->func(s->context, buffer, len);
|
|
for (i = 0; i < y; i++) {
|
|
stbiw__write_hdr_scanline(
|
|
s, x, comp, scratch,
|
|
data + comp * x * (stbi__flip_vertically_on_write ? y - 1 - i : i));
|
|
}
|
|
free(scratch);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
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 = {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 = {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);
|
|
return r;
|
|
} else
|
|
return 0;
|
|
}
|