mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-02 08:28:01 +02:00
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>
This commit is contained in:
parent
a34cb719cd
commit
1df0a68460
1 changed files with 97 additions and 11 deletions
108
src/cairo-png.c
108
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_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:
|
||||
case CAIRO_FORMAT_RGB96F:
|
||||
case CAIRO_FORMAT_RGBA128F:
|
||||
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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue