summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaarten Lankhorst <maarten.lankhorst@linux.intel.com>2018-12-03 15:13:41 +0100
committerBryce Harrington <bryce@bryceharrington.org>2019-01-07 19:14:13 -0800
commit1df0a68460ed0c9e16089919002d8c58faccdab5 (patch)
tree6deb97f8f2870077f0eefa569cb85121ca2f2106
parenta34cb719cd9cb4f0c5b78be80b80ab0ae22464a6 (diff)
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>
-rw-r--r--src/cairo-png.c108
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);