Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stb_image_write: support for 16 bit png #1726

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 191 additions & 0 deletions stb_image_write.h
Original file line number Diff line number Diff line change
Expand Up @@ -1211,6 +1211,172 @@ STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int s
return out;
}


static unsigned short stbiw__paeth_16(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 (a& 0xffff);
if (pb <= pc) return (b & 0xffff);
return (c & 0xffff);
}

signed short stbiw_swap_bit(unsigned short d)
{
return ((d >> 8) & 0xff) | ((d << 8) & 0xff00);
}

static void stbiw__encode_png_line_16(unsigned short* pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed short* line_buffer)
{
static int mapping[] = { 0,1,2,3,4 };
static int firstmap[] = { 0,1,0,5,6 };
int* mymap = (y != 0) ? mapping : firstmap;
int i;
int type = mymap[filter_type];
unsigned short* z = (unsigned short*)(pixels + width*n * (stbi__flip_vertically_on_write ? height - 1 - y : y));
int signed_stride = stbi__flip_vertically_on_write ? -width * n : width * n;
bool multi_channel = n > 1;
if (type == 0) {
if (multi_channel){
for (i = 0; i < width * n;++i)
line_buffer[i] = stbiw_swap_bit(z[i]);
}
else
memcpy(line_buffer, z, stride_bytes);
return;
}

if (multi_channel){
// first loop isn't optimized since it's just one pixel
for (i = 0; i < n; ++i) {
switch (type) {
case 1: line_buffer[i] = stbiw_swap_bit(z[i]); break;
case 2: line_buffer[i] = stbiw_swap_bit(z[i]) - stbiw_swap_bit(z[i - signed_stride]); break;
case 3: line_buffer[i] = stbiw_swap_bit(z[i]) - stbiw_swap_bit((z[i - signed_stride] >> 1)); break;
case 4: line_buffer[i] = (signed short)(stbiw_swap_bit(z[i]) - stbiw__paeth_16(0, stbiw_swap_bit(z[i - signed_stride]), 0)); break;
case 5: line_buffer[i] = stbiw_swap_bit(z[i]); break;
case 6: line_buffer[i] = stbiw_swap_bit(z[i]); break;
}
}
switch (type) {
case 1: for (i = n; i < width * n; ++i) line_buffer[i] = stbiw_swap_bit(z[i]) - stbiw_swap_bit(z[i - n]); break;
case 2: for (i = n; i < width * n; ++i) line_buffer[i] = stbiw_swap_bit(z[i]) - stbiw_swap_bit(z[i - signed_stride]); break;
case 3: for (i = n; i < width * n; ++i) line_buffer[i] = stbiw_swap_bit(z[i]) - ((stbiw_swap_bit(z[i - n]) + stbiw_swap_bit(z[i - signed_stride])) >> 1); break;
case 4: for (i = n; i < width * n; ++i) line_buffer[i] = stbiw_swap_bit(z[i]) - stbiw__paeth_16(stbiw_swap_bit(z[i - n]), stbiw_swap_bit(z[i - signed_stride]), stbiw_swap_bit(z[i - signed_stride - n])); break;
case 5: for (i = n; i < width * n; ++i) line_buffer[i] = stbiw_swap_bit(z[i]) - stbiw_swap_bit((z[i - n]) >> 1); break;
case 6: for (i = n; i < width * n; ++i) line_buffer[i] = stbiw_swap_bit(z[i]) - stbiw__paeth_16(stbiw_swap_bit(z[i - n]), 0, 0); break;
}
}
else{
// first loop isn't optimized since it's just one pixel
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 short)(z[i] - stbiw__paeth_16(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_16(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_16(z[i - n], 0, 0); break;
}
}
}


STBIWDEF unsigned char* stbi_write_png_to_mem_16(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 short* line_buffer;
int j, zlen;

if (stride_bytes == 0)
stride_bytes = x * n *2;

if (force_filter >= 5) {
force_filter = -1;
}

filt = (unsigned char*)STBIW_MALLOC((x * n*2 + 1) * y); if (!filt) return 0;
line_buffer = (signed short*)STBIW_MALLOC(x * n*2); if (!line_buffer) { STBIW_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_16((unsigned short*)(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_16((unsigned short*)(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(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_16((unsigned short*)(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*2 + 1)] = (unsigned char)filter_type;
STBIW_MEMMOVE(filt + j * (x * n*2 + 1) + 1, line_buffer, x * n*2);
}
STBIW_FREE(line_buffer);
zlib = stbi_zlib_compress(filt, y * (x * n*2 + 1), &zlen, stbi_write_png_compression_level);
STBIW_FREE(filt);
if (!zlib) return 0;

// each tag requires 12 bytes of overhead
out = (unsigned char*)STBIW_MALLOC(8 + 12 + 13 + 12 + zlen + 12);
if (!out) return 0;
*out_len = 8 + 12 + 13 + 12 + zlen + 12;

o = out;
STBIW_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++ = 16;
*o++ = STBIW_UCHAR(ctype[n]);
*o++ = 0;
*o++ = 0;
*o++ = 0;
stbiw__wpcrc(&o, 13);

stbiw__wp32(o, zlen);
stbiw__wptag(o, "IDAT");
STBIW_MEMMOVE(o, zlib, zlen);
o += zlen;
STBIW_FREE(zlib);
stbiw__wpcrc(&o, zlen);

stbiw__wp32(o, 0);
stbiw__wptag(o, "IEND");
stbiw__wpcrc(&o, 0);

STBIW_ASSERT(o == out + *out_len);

return out;
}

#ifndef STBI_WRITE_NO_STDIO
STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes)
{
Expand All @@ -1226,6 +1392,21 @@ STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const
STBIW_FREE(png);
return 1;
}

STBIWDEF int stbi_write_png_16(char const* filename, int x, int y, int comp, const void* data, int stride_bytes)
{
FILE* f;
int len;
unsigned char* png = stbi_write_png_to_mem_16((const unsigned char*)data, stride_bytes, x, y, comp, &len);
if (png == NULL) return 0;

f = stbiw__fopen(filename, "wb");
if (!f) { STBIW_FREE(png); return 0; }
fwrite(png, 1, len, f);
fclose(f);
STBIW_FREE(png);
return 1;
}
#endif

STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes)
Expand All @@ -1238,6 +1419,16 @@ STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x,
return 1;
}

STBIWDEF int stbi_write_png_to_func_16(stbi_write_func* func, void* context, int x, int y, int comp, const void* data, int stride_bytes)
{
int len;
unsigned char* png = stbi_write_png_to_mem_16((const unsigned char*)data, stride_bytes, x, y, comp, &len);
if (png == NULL) return 0;
func(context, png, len);
STBIW_FREE(png);
return 1;
}


/* ***************************************************************************
*
Expand Down