diff options
author | Maarten Lankhorst <maarten.lankhorst@linux.intel.com> | 2018-12-03 15:13:41 +0100 |
---|---|---|
committer | Bryce Harrington <bryce@bryceharrington.org> | 2019-01-07 19:14:13 -0800 |
commit | 1df0a68460ed0c9e16089919002d8c58faccdab5 (patch) | |
tree | 6deb97f8f2870077f0eefa569cb85121ca2f2106 /src/cairo-png.c | |
parent | a34cb719cd9cb4f0c5b78be80b80ab0ae22464a6 (diff) | |
download | cairo-1df0a68460ed0c9e16089919002d8c58faccdab5.tar.gz |
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: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Reviewed-by: Bryce Harrington <bryce@bryceharrington.org>
Diffstat (limited to 'src/cairo-png.c')
-rw-r--r-- | src/cairo-png.c | 108 |
1 files changed, 97 insertions, 11 deletions
diff --git a/src/cairo-png.c b/src/cairo-png.c index b9fc9160a..2e0520ae8 100644 --- a/src/cairo-png.c +++ b/src/cairo-png.c @@ -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); |