16 bit PNG write support

This commit adds a support of saving 16bit PNG files.

Alpha for such files would be premultiplied, would be corrected
with an upcoming alpha premul cleanup (it's not the only format
which will output 16bit image with premul alpha).
This commit is contained in:
Sergey Sharybin 2012-12-30 13:01:47 +00:00
parent fde101c50c
commit f62fc79da0
4 changed files with 102 additions and 28 deletions

@ -1119,6 +1119,8 @@ char BKE_imtype_valid_depths(const char imtype)
return R_IMF_CHAN_DEPTH_10;
case R_IMF_IMTYPE_JP2:
return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16;
case R_IMF_IMTYPE_PNG:
return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_16;
/* most formats are 8bit only */
default:
return R_IMF_CHAN_DEPTH_8;
@ -1313,9 +1315,13 @@ void BKE_imbuf_to_image_format(struct ImageFormatData *im_format, const ImBuf *i
im_format->imtype = R_IMF_IMTYPE_RADHDR;
#endif
else if (ftype == PNG)
else if (ftype == PNG) {
im_format->imtype = R_IMF_IMTYPE_PNG;
if (custom_flags & PNG_16BIT)
im_format->depth = R_IMF_CHAN_DEPTH_16;
}
#ifdef WITH_DDS
else if (ftype == DDS)
im_format->imtype = R_IMF_IMTYPE_DDS;
@ -1847,8 +1853,12 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf)
else if (ELEM5(imtype, R_IMF_IMTYPE_PNG, R_IMF_IMTYPE_FFMPEG, R_IMF_IMTYPE_H264, R_IMF_IMTYPE_THEORA, R_IMF_IMTYPE_XVID)) {
ibuf->ftype = PNG;
if (imtype == R_IMF_IMTYPE_PNG)
if (imtype == R_IMF_IMTYPE_PNG) {
if (imf->depth == R_IMF_CHAN_DEPTH_16)
ibuf->ftype |= PNG_16BIT;
ibuf->ftype |= compress;
}
}
#ifdef WITH_DDS

@ -172,9 +172,9 @@ typedef struct ImBuf {
/*
* The bit flag is stored in the ImBuf.ftype variable.
* Note that the lower 10 bits is used for storing custom flags
* Note that the lower 11 bits is used for storing custom flags
*/
#define IB_CUSTOM_FLAGS_MASK 0x3ff
#define IB_CUSTOM_FLAGS_MASK 0x400
#define PNG (1 << 30)
#define TGA (1 << 28)
@ -221,6 +221,8 @@ typedef struct ImBuf {
#define JP2_J2K (1 << 11)
#endif
#define PNG_16BIT (1 << 10)
#define RAWTGA (TGA | 1)
#define JPG_STD (JPG | (0 << 8))

@ -69,7 +69,7 @@ void quicktime_exit(void);
ImFileType IMB_FILE_TYPES[] = {
{NULL, NULL, imb_is_a_jpeg, imb_ftype_default, imb_load_jpeg, imb_savejpeg, NULL, 0, JPG, COLOR_ROLE_DEFAULT_BYTE},
{NULL, NULL, imb_is_a_png, imb_ftype_default, imb_loadpng, imb_savepng, NULL, 0, PNG, COLOR_ROLE_DEFAULT_BYTE},
{NULL, NULL, imb_is_a_png, imb_ftype_default, imb_loadpng, imb_savepng, NULL, IM_FTYPE_FLOAT, PNG, COLOR_ROLE_DEFAULT_BYTE},
{NULL, NULL, imb_is_a_bmp, imb_ftype_default, imb_bmp_decode, imb_savebmp, NULL, 0, BMP, COLOR_ROLE_DEFAULT_BYTE},
{NULL, NULL, imb_is_a_targa, imb_ftype_default, imb_loadtarga, imb_savetarga, NULL, 0, TGA, COLOR_ROLE_DEFAULT_BYTE},
{NULL, NULL, imb_is_a_iris, imb_ftype_iris, imb_loadiris, imb_saveiris, NULL, 0, IMAGIC, COLOR_ROLE_DEFAULT_BYTE},

@ -109,10 +109,14 @@ int imb_savepng(struct ImBuf *ibuf, const char *name, int flags)
unsigned char *pixels = NULL;
unsigned char *from, *to;
unsigned short *pixels16 = NULL, *to16;
float *from_float;
png_bytepp row_pointers = NULL;
int i, bytesperpixel, color_type = PNG_COLOR_TYPE_GRAY;
FILE *fp = NULL;
int is_16bit = (ibuf->ftype & PNG_16BIT) && ibuf->rect_float;
/* use the jpeg quality setting for compression */
int compression;
compression = (int)(((float)(ibuf->ftype & 0xff) / 11.1111f));
@ -150,8 +154,12 @@ int imb_savepng(struct ImBuf *ibuf, const char *name, int flags)
/* copy image data */
pixels = MEM_mallocN(ibuf->x * ibuf->y * bytesperpixel * sizeof(unsigned char), "pixels");
if (pixels == NULL) {
if (is_16bit)
pixels16 = MEM_mallocN(ibuf->x * ibuf->y * bytesperpixel * sizeof(unsigned short), "png 16bit pixels");
else
pixels = MEM_mallocN(ibuf->x * ibuf->y * bytesperpixel * sizeof(unsigned char), "png 8bit pixels");
if (pixels == NULL && pixels16 == NULL) {
png_destroy_write_struct(&png_ptr, &info_ptr);
printf("imb_savepng: Cannot allocate pixels array of %dx%d, %d bytes per pixel for file: '%s'\n", ibuf->x, ibuf->y, bytesperpixel, name);
return 0;
@ -159,32 +167,63 @@ int imb_savepng(struct ImBuf *ibuf, const char *name, int flags)
from = (unsigned char *) ibuf->rect;
to = pixels;
from_float = ibuf->rect_float;
to16 = pixels16;
switch (bytesperpixel) {
case 4:
color_type = PNG_COLOR_TYPE_RGBA;
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to[0] = from[0];
to[1] = from[1];
to[2] = from[2];
to[3] = from[3];
to += 4; from += 4;
if (is_16bit) {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to16[0] = FTOUSHORT(from_float[0]);
to16[1] = FTOUSHORT(from_float[1]);
to16[2] = FTOUSHORT(from_float[2]);
to16[3] = FTOUSHORT(from_float[3]);
to16 += 4; from_float += 4;
}
}
else {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to[0] = from[0];
to[1] = from[1];
to[2] = from[2];
to[3] = from[3];
to += 4; from += 4;
}
}
break;
case 3:
color_type = PNG_COLOR_TYPE_RGB;
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to[0] = from[0];
to[1] = from[1];
to[2] = from[2];
to += 3; from += 4;
if (is_16bit) {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to16[0] = FTOUSHORT(from_float[0]);
to16[1] = FTOUSHORT(from_float[1]);
to16[2] = FTOUSHORT(from_float[2]);
to16 += 3; from_float += 4;
}
}
else {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to[0] = from[0];
to[1] = from[1];
to[2] = from[2];
to += 3; from += 4;
}
}
break;
case 1:
color_type = PNG_COLOR_TYPE_GRAY;
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to[0] = from[0];
to++; from += 4;
if (is_16bit) {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to16[0] = FTOUSHORT(from_float[0]);
to16++; from_float += 4;
}
}
else {
for (i = ibuf->x * ibuf->y; i > 0; i--) {
to[0] = from[0];
to++; from += 4;
}
}
break;
}
@ -203,7 +242,10 @@ int imb_savepng(struct ImBuf *ibuf, const char *name, int flags)
fp = BLI_fopen(name, "wb");
if (!fp) {
png_destroy_write_struct(&png_ptr, &info_ptr);
MEM_freeN(pixels);
if (pixels)
MEM_freeN(pixels);
if (pixels16)
MEM_freeN(pixels16);
printf("imb_savepng: Cannot open file for writing: '%s'\n", name);
return 0;
}
@ -227,7 +269,7 @@ int imb_savepng(struct ImBuf *ibuf, const char *name, int flags)
info_ptr,
ibuf->x,
ibuf->y,
8,
is_16bit ? 16 : 8,
color_type,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
@ -268,12 +310,19 @@ int imb_savepng(struct ImBuf *ibuf, const char *name, int flags)
/* write the file header information */
png_write_info(png_ptr, info_ptr);
#ifdef __LITTLE_ENDIAN__
png_set_swap(png_ptr);
#endif
/* allocate memory for an array of row-pointers */
row_pointers = (png_bytepp) MEM_mallocN(ibuf->y * sizeof(png_bytep), "row_pointers");
if (row_pointers == NULL) {
printf("imb_savepng: Cannot allocate row-pointers array for file '%s'\n", name);
png_destroy_write_struct(&png_ptr, &info_ptr);
MEM_freeN(pixels);
if (pixels)
MEM_freeN(pixels);
if (pixels16)
MEM_freeN(pixels16);
if (fp) {
fclose(fp);
}
@ -281,9 +330,17 @@ int imb_savepng(struct ImBuf *ibuf, const char *name, int flags)
}
/* set the individual row-pointers to point at the correct offsets */
for (i = 0; i < ibuf->y; i++) {
row_pointers[ibuf->y - 1 - i] = (png_bytep)
((unsigned char *)pixels + (i * ibuf->x) * bytesperpixel * sizeof(unsigned char));
if (is_16bit) {
for (i = 0; i < ibuf->y; i++) {
row_pointers[ibuf->y - 1 - i] = (png_bytep)
((unsigned short *)pixels16 + (i * ibuf->x) * bytesperpixel);
}
}
else {
for (i = 0; i < ibuf->y; i++) {
row_pointers[ibuf->y - 1 - i] = (png_bytep)
((unsigned char *)pixels + (i * ibuf->x) * bytesperpixel * sizeof(unsigned char));
}
}
/* write out the entire image data in one call */
@ -293,7 +350,10 @@ int imb_savepng(struct ImBuf *ibuf, const char *name, int flags)
png_write_end(png_ptr, info_ptr);
/* clean up */
MEM_freeN(pixels);
if (pixels)
MEM_freeN(pixels);
if (pixels16)
MEM_freeN(pixels16);
MEM_freeN(row_pointers);
png_destroy_write_struct(&png_ptr, &info_ptr);
@ -394,6 +454,8 @@ ImBuf *imb_loadpng(unsigned char *mem, size_t size, int flags, char colorspace[I
if (ibuf) {
ibuf->ftype = PNG;
if (bit_depth == 16)
ibuf->ftype |= PNG_16BIT;
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) {
int unit_type;