aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fastiv-io.c153
-rw-r--r--fastiv-view.c4
2 files changed, 117 insertions, 40 deletions
diff --git a/fastiv-io.c b/fastiv-io.c
index d63d336..d02c2fe 100644
--- a/fastiv-io.c
+++ b/fastiv-io.c
@@ -50,6 +50,10 @@
#include "xdg.h"
#include "fastiv-io.h"
+#if CAIRO_VERSION >= 11702 && X11_ACTUALLY_SUPPORTS_RGBA128F_OR_WE_USE_OPENGL
+#define FASTIV_CAIRO_RGBA128F
+#endif
+
// A subset of shared-mime-info that produces an appropriate list of
// file extensions. Chiefly motivated by the suckiness of RAW images:
// someone else will maintain the list of file extensions for us.
@@ -112,27 +116,52 @@ open_wuffs(
return NULL;
}
- // CAIRO_FORMAT_ARGB32: "The 32-bit quantities are stored native-endian.
- // Pre-multiplied alpha is used."
- // CAIRO_FORMAT_RGB{24,30}: analogous, not going to use these so far.
- //
- // Wuffs: /doc/note/pixel-formats.md specifies it as "memory order", which,
- // for our purposes, means big endian. Currently useful formats, as per
- // support within wuffs_base__pixel_swizzler__prepare__*():
- // - WUFFS_BASE__PIXEL_FORMAT__ARGB_PREMUL: big-endian
- // - WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL: little-endian
- // - WUFFS_BASE__PIXEL_FORMAT__XRGB: big-endian
- // - WUFFS_BASE__PIXEL_FORMAT__BGRX: little-endian
+ // Wuffs maps tRNS to BGRA in `decoder.decode_trns?`, we should be fine.
+ // wuffs_base__pixel_format__transparency() doesn't reflect the image file.
+ bool opaque = wuffs_base__image_config__first_frame_is_opaque(&cfg);
+
+ // Wuffs' API is kind of awful--we want to catch deep RGB and deep grey.
+ wuffs_base__pixel_format srcfmt =
+ wuffs_base__pixel_config__pixel_format(&cfg.pixcfg);
+ uint32_t bpp = wuffs_base__pixel_format__bits_per_pixel(&srcfmt);
+
+ // Cairo doesn't support transparency with RGB30, so no premultiplication.
+ bool pack_16_10 = opaque && (bpp > 24 || (bpp < 24 && bpp > 8));
+#ifdef FASTIV_CAIRO_RGBA128F
+ bool expand_16_float = !opaque && (bpp > 24 || (bpp < 24 && bpp > 8));
+#endif // FASTIV_CAIRO_RGBA128F
+
+ // In Wuffs, /doc/note/pixel-formats.md declares "memory order", which,
+ // for our purposes, means big endian, and BGRA results in 32-bit ARGB
+ // on most machines.
//
- // TODO(p): Make Wuffs support RGB30 as a destination format,
- // so far we only have WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL_4X16LE
- // and in general, 16-bit depth swizzlers are stubbed.
- wuffs_base__pixel_config__set(&cfg.pixcfg,
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
- WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL,
-#else
- WUFFS_BASE__PIXEL_FORMAT__ARGB_PREMUL,
-#endif
+ // XXX: WUFFS_BASE__PIXEL_FORMAT__ARGB_PREMUL is not expressible, only RGBA.
+ // Wuffs doesn't support big-endian architectures at all, we might want to
+ // fall back to spng in such cases, or do a second conversion.
+ uint32_t wuffs_format = WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL;
+
+ // CAIRO_FORMAT_ARGB32: "The 32-bit quantities are stored native-endian.
+ // Pre-multiplied alpha is used." CAIRO_FORMAT_RGB{24,30} are analogous.
+ cairo_format_t cairo_format = CAIRO_FORMAT_ARGB32;
+
+#ifdef FASTIV_CAIRO_RGBA128F
+ if (expand_16_float) {
+ wuffs_format = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL_4X16LE;
+ cairo_format = CAIRO_FORMAT_RGBA128F;
+ } else
+#endif // FASTIV_CAIRO_RGBA128F
+ if (pack_16_10) {
+ // TODO(p): Make Wuffs support RGB30 as a destination format;
+ // in general, 16-bit depth swizzlers are stubbed.
+ // See also wuffs_base__pixel_swizzler__prepare__*().
+ wuffs_format = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL_4X16LE;
+ cairo_format = CAIRO_FORMAT_RGB30;
+ } else if (opaque) {
+ wuffs_format = WUFFS_BASE__PIXEL_FORMAT__BGRX;
+ cairo_format = CAIRO_FORMAT_RGB24;
+ }
+
+ wuffs_base__pixel_config__set(&cfg.pixcfg, wuffs_format,
WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, width, height);
wuffs_base__slice_u8 workbuf = {0};
@@ -146,29 +175,42 @@ open_wuffs(
}
}
- cairo_surface_t *surface =
- cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
+ unsigned char *targetbuf = NULL;
+ cairo_surface_t *result = NULL, *surface =
+ cairo_image_surface_create(cairo_format, width, height);
cairo_status_t surface_status = cairo_surface_status(surface);
if (surface_status != CAIRO_STATUS_SUCCESS) {
set_error(error, cairo_status_to_string(surface_status));
- cairo_surface_destroy(surface);
- free(workbuf.ptr);
- return NULL;
+ goto fail;
}
// CAIRO_STRIDE_ALIGNMENT is 4 bytes, so there will be no padding with
// ARGB/BGR/XRGB/BGRX. This function does not support a stride different
// from the width, maybe Wuffs internals do not either.
+ unsigned char *surface_data = cairo_image_surface_get_data(surface);
+ int surface_stride = cairo_image_surface_get_stride(surface);
wuffs_base__pixel_buffer pb = {0};
- status = wuffs_base__pixel_buffer__set_from_slice(&pb, &cfg.pixcfg,
- wuffs_base__make_slice_u8(cairo_image_surface_get_data(surface),
- cairo_image_surface_get_stride(surface) *
- cairo_image_surface_get_height(surface)));
+#ifdef FASTIV_CAIRO_RGBA128F
+ if (expand_16_float) {
+ uint32_t targetbuf_size = height * width * 64;
+ targetbuf = g_malloc(targetbuf_size);
+ status = wuffs_base__pixel_buffer__set_from_slice(&pb, &cfg.pixcfg,
+ wuffs_base__make_slice_u8(targetbuf, targetbuf_size));
+ } else
+#endif // FASTIV_CAIRO_RGBA128F
+ if (pack_16_10) {
+ uint32_t targetbuf_size = height * width * 16;
+ targetbuf = g_malloc(targetbuf_size);
+ status = wuffs_base__pixel_buffer__set_from_slice(&pb, &cfg.pixcfg,
+ wuffs_base__make_slice_u8(targetbuf, targetbuf_size));
+ } else {
+ status = wuffs_base__pixel_buffer__set_from_slice(&pb, &cfg.pixcfg,
+ wuffs_base__make_slice_u8(surface_data,
+ surface_stride * cairo_image_surface_get_height(surface)));
+ }
if (!wuffs_base__status__is_ok(&status)) {
set_error(error, wuffs_base__status__message(&status));
- cairo_surface_destroy(surface);
- free(workbuf.ptr);
- return NULL;
+ goto fail;
}
#if 0 // We're not using this right now.
@@ -176,9 +218,7 @@ open_wuffs(
status = wuffs_png__decoder__decode_frame_config(&dec, &fc, &src);
if (!wuffs_base__status__is_ok(&status)) {
set_error(error, wuffs_base__status__message(&status));
- cairo_surface_destroy(surface);
- free(workbuf.ptr);
- return NULL;
+ goto fail;
}
#endif
@@ -189,16 +229,49 @@ open_wuffs(
dec, &pb, &src, WUFFS_BASE__PIXEL_BLEND__SRC, workbuf, NULL);
if (!wuffs_base__status__is_ok(&status)) {
set_error(error, wuffs_base__status__message(&status));
- cairo_surface_destroy(surface);
- free(workbuf.ptr);
- return NULL;
+ goto fail;
+ }
+
+#ifdef FASTIV_CAIRO_RGBA128F
+ if (expand_16_float) {
+ g_debug("Wuffs to Cairo RGBA128F");
+ uint16_t *in = (uint16_t *) targetbuf;
+ float *out = (float *) surface_data;
+ for (uint32_t y = 0; y < height; y++) {
+ for (uint32_t x = 0; x < width; x++) {
+ float b = *in++ / 65535., g = *in++ / 65535.,
+ r = *in++ / 65535., a = *in++ / 65535.;
+ *out++ = r * a;
+ *out++ = g * a;
+ *out++ = b * a;
+ *out++ = a;
+ }
+ }
+ } else
+#endif // FASTIV_CAIRO_RGBA128F
+ if (pack_16_10) {
+ g_debug("Wuffs to Cairo RGB30");
+ uint16_t *in = (uint16_t *) targetbuf;
+ uint32_t *out = (uint32_t *) surface_data;
+ for (uint32_t y = 0; y < height; y++) {
+ for (uint32_t x = 0; x < width; x++) {
+ uint16_t b = *in++, g = *in++, r = *in++, x = *in++;
+ *out++ = (x >> 14) << 30 |
+ (r >> 6) << 20 | (g >> 6) << 10 | (b >> 6);
+ }
+ }
}
// Pixel data has been written, need to let Cairo know.
- cairo_surface_mark_dirty(surface);
+ cairo_surface_mark_dirty((result = surface));
+fail:
+ if (!result)
+ cairo_surface_destroy(surface);
+
+ g_free(targetbuf);
free(workbuf.ptr);
- return surface;
+ return result;
}
static cairo_surface_t *
diff --git a/fastiv-view.c b/fastiv-view.c
index 700dc9b..a9ba5cf 100644
--- a/fastiv-view.c
+++ b/fastiv-view.c
@@ -181,6 +181,10 @@ fastiv_view_realize(GtkWidget *widget)
gtk_widget_register_window(widget, window);
gtk_widget_set_window(widget, window);
gtk_widget_set_realized(widget, TRUE);
+
+ // Without the following call, or the rendering mode set to "recording",
+ // RGB30 degrades to RGB24.
+ gdk_window_ensure_native(window);
}
static gboolean