Compare commits

..

No commits in common. "11b796945941d33beff624dabdbb20fbc0aabe3c" and "37adaac965d2dc7cbb28c1068c382521a30092b6" have entirely different histories.

2 changed files with 71 additions and 143 deletions

View File

@ -50,10 +50,6 @@
#include "xdg.h" #include "xdg.h"
#include "fastiv-io.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 // A subset of shared-mime-info that produces an appropriate list of
// file extensions. Chiefly motivated by the suckiness of RAW images: // file extensions. Chiefly motivated by the suckiness of RAW images:
// someone else will maintain the list of file extensions for us. // someone else will maintain the list of file extensions for us.
@ -116,52 +112,27 @@ open_wuffs(
return NULL; return NULL;
} }
// 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.
//
// 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. // CAIRO_FORMAT_ARGB32: "The 32-bit quantities are stored native-endian.
// Pre-multiplied alpha is used." CAIRO_FORMAT_RGB{24,30} are analogous. // Pre-multiplied alpha is used."
cairo_format_t cairo_format = CAIRO_FORMAT_ARGB32; // CAIRO_FORMAT_RGB{24,30}: analogous, not going to use these so far.
//
#ifdef FASTIV_CAIRO_RGBA128F // Wuffs: /doc/note/pixel-formats.md specifies it as "memory order", which,
if (expand_16_float) { // for our purposes, means big endian. Currently useful formats, as per
wuffs_format = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL_4X16LE; // support within wuffs_base__pixel_swizzler__prepare__*():
cairo_format = CAIRO_FORMAT_RGBA128F; // - WUFFS_BASE__PIXEL_FORMAT__ARGB_PREMUL: big-endian
} else // - WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL: little-endian
#endif // FASTIV_CAIRO_RGBA128F // - WUFFS_BASE__PIXEL_FORMAT__XRGB: big-endian
if (pack_16_10) { // - WUFFS_BASE__PIXEL_FORMAT__BGRX: little-endian
// TODO(p): Make Wuffs support RGB30 as a destination format; //
// in general, 16-bit depth swizzlers are stubbed. // TODO(p): Make Wuffs support RGB30 as a destination format,
// See also wuffs_base__pixel_swizzler__prepare__*(). // so far we only have WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL_4X16LE
wuffs_format = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL_4X16LE; // and in general, 16-bit depth swizzlers are stubbed.
cairo_format = CAIRO_FORMAT_RGB30; wuffs_base__pixel_config__set(&cfg.pixcfg,
} else if (opaque) { #if G_BYTE_ORDER == G_LITTLE_ENDIAN
wuffs_format = WUFFS_BASE__PIXEL_FORMAT__BGRX; WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL,
cairo_format = CAIRO_FORMAT_RGB24; #else
} WUFFS_BASE__PIXEL_FORMAT__ARGB_PREMUL,
#endif
wuffs_base__pixel_config__set(&cfg.pixcfg, wuffs_format,
WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, width, height); WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, width, height);
wuffs_base__slice_u8 workbuf = {0}; wuffs_base__slice_u8 workbuf = {0};
@ -175,42 +146,29 @@ open_wuffs(
} }
} }
unsigned char *targetbuf = NULL; cairo_surface_t *surface =
cairo_surface_t *result = NULL, *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
cairo_image_surface_create(cairo_format, width, height);
cairo_status_t surface_status = cairo_surface_status(surface); cairo_status_t surface_status = cairo_surface_status(surface);
if (surface_status != CAIRO_STATUS_SUCCESS) { if (surface_status != CAIRO_STATUS_SUCCESS) {
set_error(error, cairo_status_to_string(surface_status)); set_error(error, cairo_status_to_string(surface_status));
goto fail; cairo_surface_destroy(surface);
free(workbuf.ptr);
return NULL;
} }
// CAIRO_STRIDE_ALIGNMENT is 4 bytes, so there will be no padding with // 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 // ARGB/BGR/XRGB/BGRX. This function does not support a stride different
// from the width, maybe Wuffs internals do not either. // 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}; wuffs_base__pixel_buffer pb = {0};
#ifdef FASTIV_CAIRO_RGBA128F status = wuffs_base__pixel_buffer__set_from_slice(&pb, &cfg.pixcfg,
if (expand_16_float) { wuffs_base__make_slice_u8(cairo_image_surface_get_data(surface),
uint32_t targetbuf_size = height * width * 64; cairo_image_surface_get_stride(surface) *
targetbuf = g_malloc(targetbuf_size); cairo_image_surface_get_height(surface)));
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)) { if (!wuffs_base__status__is_ok(&status)) {
set_error(error, wuffs_base__status__message(&status)); set_error(error, wuffs_base__status__message(&status));
goto fail; cairo_surface_destroy(surface);
free(workbuf.ptr);
return NULL;
} }
#if 0 // We're not using this right now. #if 0 // We're not using this right now.
@ -218,7 +176,9 @@ open_wuffs(
status = wuffs_png__decoder__decode_frame_config(&dec, &fc, &src); status = wuffs_png__decoder__decode_frame_config(&dec, &fc, &src);
if (!wuffs_base__status__is_ok(&status)) { if (!wuffs_base__status__is_ok(&status)) {
set_error(error, wuffs_base__status__message(&status)); set_error(error, wuffs_base__status__message(&status));
goto fail; cairo_surface_destroy(surface);
free(workbuf.ptr);
return NULL;
} }
#endif #endif
@ -229,49 +189,16 @@ open_wuffs(
dec, &pb, &src, WUFFS_BASE__PIXEL_BLEND__SRC, workbuf, NULL); dec, &pb, &src, WUFFS_BASE__PIXEL_BLEND__SRC, workbuf, NULL);
if (!wuffs_base__status__is_ok(&status)) { if (!wuffs_base__status__is_ok(&status)) {
set_error(error, wuffs_base__status__message(&status)); set_error(error, wuffs_base__status__message(&status));
goto fail; cairo_surface_destroy(surface);
} free(workbuf.ptr);
return NULL;
#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. // Pixel data has been written, need to let Cairo know.
cairo_surface_mark_dirty((result = surface)); cairo_surface_mark_dirty(surface);
fail:
if (!result)
cairo_surface_destroy(surface);
g_free(targetbuf);
free(workbuf.ptr); free(workbuf.ptr);
return result; return surface;
} }
static cairo_surface_t * static cairo_surface_t *
@ -718,12 +645,12 @@ read_spng_thumbnail(
const gchar *path, const gchar *uri, time_t mtime, GError **error) const gchar *path, const gchar *uri, time_t mtime, GError **error)
{ {
FILE *fp; FILE *fp;
cairo_surface_t *result = NULL;
if (!(fp = fopen(path, "rb"))) { if (!(fp = fopen(path, "rb"))) {
set_error(error, g_strerror(errno)); set_error(error, g_strerror(errno));
return NULL; return NULL;
} }
cairo_surface_t *result = NULL;
errno = 0; errno = 0;
spng_ctx *ctx = spng_ctx_new(0); spng_ctx *ctx = spng_ctx_new(0);
if (!ctx) { if (!ctx) {
@ -744,22 +671,7 @@ read_spng_thumbnail(
goto fail; goto fail;
} }
struct spng_ihdr ihdr = {}; uint32_t *data = g_malloc0(size);
spng_get_ihdr(ctx, &ihdr);
cairo_surface_t *surface = cairo_image_surface_create(
CAIRO_FORMAT_ARGB32, ihdr.width, ihdr.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));
goto fail_data;
}
uint32_t *data = (uint32_t *) cairo_image_surface_get_data(surface);
g_assert((size_t) cairo_image_surface_get_stride(surface) *
cairo_image_surface_get_height(surface) == size);
cairo_surface_flush(surface);
if ((err = spng_decode_image(ctx, data, size, SPNG_FMT_RGBA8, if ((err = spng_decode_image(ctx, data, size, SPNG_FMT_RGBA8,
SPNG_DECODE_TRNS | SPNG_DECODE_GAMMA))) { SPNG_DECODE_TRNS | SPNG_DECODE_GAMMA))) {
set_error(error, spng_strerror(err)); set_error(error, spng_strerror(err));
@ -774,31 +686,51 @@ read_spng_thumbnail(
goto fail_data; goto fail_data;
} }
struct spng_ihdr ihdr = {};
spng_get_ihdr(ctx, &ihdr);
cairo_surface_t *surface = cairo_image_surface_create(
CAIRO_FORMAT_ARGB32, ihdr.width, ihdr.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);
goto fail_data;
}
g_assert((size_t) cairo_image_surface_get_stride(surface) *
cairo_image_surface_get_height(surface) == size);
// pixman can be mildly abused to do this operation, but it won't be faster. // pixman can be mildly abused to do this operation, but it won't be faster.
uint32_t *output = (uint32_t *) cairo_image_surface_get_data(surface);
cairo_surface_flush(surface);
struct spng_trns trns = {}; struct spng_trns trns = {};
if (ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA || if (ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA ||
ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA || ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA ||
!spng_get_trns(ctx, &trns)) { !spng_get_trns(ctx, &trns)) {
for (size_t i = size / sizeof *data; i--; ) { for (size_t i = size / sizeof *output; i--; ) {
const uint8_t *unit = (const uint8_t *) &data[i], uint8_t *unit = (uint8_t *) &data[i],
a = unit[3], a = unit[3],
b = unit[2] * a / 255, b = unit[2] * a / 255,
g = unit[1] * a / 255, g = unit[1] * a / 255,
r = unit[0] * a / 255; r = unit[0] * a / 255;
data[i] = a << 24 | r << 16 | g << 8 | b; output[i] = a << 24 | r << 16 | g << 8 | b;
} }
} else { } else {
for (size_t i = size / sizeof *data; i--; ) { for (size_t i = size / sizeof *output; i--; ) {
uint32_t rgba = g_ntohl(data[i]); uint8_t *unit = (uint8_t *) &data[i],
data[i] = rgba << 24 | rgba >> 8; a = unit[3],
b = unit[2],
g = unit[1],
r = unit[0];
output[i] = a << 24 | r << 16 | g << 8 | b;
} }
} }
cairo_surface_mark_dirty((result = surface)); cairo_surface_mark_dirty((result = surface));
fail_data: fail_data:
if (!result) free(data);
cairo_surface_destroy(surface);
fail: fail:
spng_ctx_free(ctx); spng_ctx_free(ctx);
fail_init: fail_init:

View File

@ -181,10 +181,6 @@ fastiv_view_realize(GtkWidget *widget)
gtk_widget_register_window(widget, window); gtk_widget_register_window(widget, window);
gtk_widget_set_window(widget, window); gtk_widget_set_window(widget, window);
gtk_widget_set_realized(widget, TRUE); 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 static gboolean