Commit 1df0a684 authored by Maarten Lankhorst's avatar Maarten Lankhorst Committed by Bryce Harrington

png: Add support for writing new floating point formats as 16 bpc png.

_cairo_image_surface_coerce will round down the image to a lower
bpp when using one of the floating point formats, so don't coerce those.
This makes the code actually work for those formats.

Because a float takes more storage than u16, we have to convert float
to u16 before calling png_write_image, because png_write
doesn't give us back the original row data, but an in-place copy.

With these changes we can dump floating point files with the highest
possible accuracy, with floats clamped between 0 and 1.
Signed-off-by: default avatarMaarten Lankhorst <maarten.lankhorst@linux.intel.com>
Reviewed-by: Bryce Harrington's avatarBryce Harrington <bryce@bryceharrington.org>
parent a34cb719
......@@ -105,6 +105,57 @@ unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data)
}
}
static uint16_t f_to_u16(float val)
{
if (val < 0)
return 0;
else if (val > 1)
return 65535;
else
return (uint16_t)(val * 65535.f);
}
static void
unpremultiply_float (float *f, uint16_t *d16, unsigned width)
{
unsigned int i;
for (i = 0; i < width; i++) {
float r, g, b, a;
r = *f++;
g = *f++;
b = *f++;
a = *f++;
if (a > 0) {
*d16++ = f_to_u16(r / a);
*d16++ = f_to_u16(g / a);
*d16++ = f_to_u16(b / a);
*d16++ = f_to_u16(a);
} else {
*d16++ = 0;
*d16++ = 0;
*d16++ = 0;
*d16++ = 0;
}
}
}
static void
convert_float_to_u16 (float *f, uint16_t *d16, unsigned int width)
{
unsigned int i;
for (i = 0; i < width; i++) {
*d16++ = f_to_u16(*f++);
*d16++ = f_to_u16(*f++);
*d16++ = f_to_u16(*f++);
*d16++ = 0;
}
}
/* Converts native endian xRGB => RGBx bytes */
static void
convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data)
......@@ -182,6 +233,7 @@ write_png (cairo_surface_t *surface,
png_color_16 white;
int png_color_type;
int bpc;
unsigned char *volatile u16_copy = NULL;
status = _cairo_surface_acquire_source_image (surface,
&image,
......@@ -198,11 +250,22 @@ write_png (cairo_surface_t *surface,
goto BAIL1;
}
/* Handle the various fallback formats (e.g. low bit-depth XServers)
* by coercing them to a simpler format using pixman.
*/
clone = _cairo_image_surface_coerce (image);
status = clone->base.status;
/* Don't coerce to a lower resolution format */
if (image->format == CAIRO_FORMAT_RGB96F ||
image->format == CAIRO_FORMAT_RGBA128F) {
u16_copy = _cairo_malloc_ab (image->width * 8, image->height);
if (!u16_copy) {
status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL1;
}
clone = (cairo_image_surface_t *)cairo_surface_reference (&image->base);
} else {
/* Handle the various fallback formats (e.g. low bit-depth XServers)
* by coercing them to a simpler format using pixman.
*/
clone = _cairo_image_surface_coerce (image);
status = clone->base.status;
}
if (unlikely (status))
goto BAIL1;
......@@ -212,8 +275,22 @@ write_png (cairo_surface_t *surface,
goto BAIL2;
}
for (i = 0; i < clone->height; i++)
rows[i] = (png_byte *) clone->data + i * clone->stride;
if (!u16_copy) {
for (i = 0; i < clone->height; i++)
rows[i] = (png_byte *)clone->data + i * clone->stride;
} else {
for (i = 0; i < clone->height; i++) {
float *float_line = (float *)&clone->data[i * clone->stride];
uint16_t *u16_line = (uint16_t *)&u16_copy[i * clone->width * 8];
if (image->format == CAIRO_FORMAT_RGBA128F)
unpremultiply_float (float_line, u16_line, clone->width);
else
convert_float_to_u16 (float_line, u16_line, clone->width);
rows[i] = (png_byte *)u16_line;
}
}
png = png_create_write_struct (PNG_LIBPNG_VER_STRING, &status,
png_simple_error_callback,
......@@ -263,10 +340,16 @@ write_png (cairo_surface_t *surface,
png_set_packswap (png);
#endif
break;
case CAIRO_FORMAT_INVALID:
case CAIRO_FORMAT_RGB16_565:
case CAIRO_FORMAT_RGB96F:
bpc = 16;
png_color_type = PNG_COLOR_TYPE_RGB;
break;
case CAIRO_FORMAT_RGBA128F:
bpc = 16;
png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
break;
case CAIRO_FORMAT_INVALID:
case CAIRO_FORMAT_RGB16_565:
default:
status = _cairo_error (CAIRO_STATUS_INVALID_FORMAT);
goto BAIL4;
......@@ -298,9 +381,11 @@ write_png (cairo_surface_t *surface,
png_write_info (png, info);
if (png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
png_set_write_user_transform_fn (png, unpremultiply_data);
if (clone->format != CAIRO_FORMAT_RGBA128F)
png_set_write_user_transform_fn (png, unpremultiply_data);
} else if (png_color_type == PNG_COLOR_TYPE_RGB) {
png_set_write_user_transform_fn (png, convert_data_to_bytes);
if (clone->format != CAIRO_FORMAT_RGB96F)
png_set_write_user_transform_fn (png, convert_data_to_bytes);
png_set_filler (png, 0, PNG_FILLER_AFTER);
}
......@@ -313,6 +398,7 @@ BAIL3:
free (rows);
BAIL2:
cairo_surface_destroy (&clone->base);
free (u16_copy);
BAIL1:
_cairo_surface_release_source_image (surface, image, image_extra);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment