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 "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.
@ -116,52 +112,27 @@ open_wuffs(
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.
// 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,
// 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
//
// 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
WUFFS_BASE__PIXEL_SUBSAMPLING__NONE, width, height);
wuffs_base__slice_u8 workbuf = {0};
@ -175,42 +146,29 @@ open_wuffs(
}
}
unsigned char *targetbuf = NULL;
cairo_surface_t *result = NULL, *surface =
cairo_image_surface_create(cairo_format, width, height);
cairo_surface_t *surface =
cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 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));
goto fail;
cairo_surface_destroy(surface);
free(workbuf.ptr);
return NULL;
}
// 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};
#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)));
}
wuffs_base__make_slice_u8(cairo_image_surface_get_data(surface),
cairo_image_surface_get_stride(surface) *
cairo_image_surface_get_height(surface)));
if (!wuffs_base__status__is_ok(&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.
@ -218,7 +176,9 @@ 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));
goto fail;
cairo_surface_destroy(surface);
free(workbuf.ptr);
return NULL;
}
#endif
@ -229,49 +189,16 @@ 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));
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);
}
}
cairo_surface_destroy(surface);
free(workbuf.ptr);
return NULL;
}
// 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);
return result;
return surface;
}
static cairo_surface_t *
@ -718,12 +645,12 @@ read_spng_thumbnail(
const gchar *path, const gchar *uri, time_t mtime, GError **error)
{
FILE *fp;
cairo_surface_t *result = NULL;
if (!(fp = fopen(path, "rb"))) {
set_error(error, g_strerror(errno));
return NULL;
}
cairo_surface_t *result = NULL;
errno = 0;
spng_ctx *ctx = spng_ctx_new(0);
if (!ctx) {
@ -744,22 +671,7 @@ read_spng_thumbnail(
goto fail;
}
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));
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);
uint32_t *data = g_malloc0(size);
if ((err = spng_decode_image(ctx, data, size, SPNG_FMT_RGBA8,
SPNG_DECODE_TRNS | SPNG_DECODE_GAMMA))) {
set_error(error, spng_strerror(err));
@ -774,31 +686,51 @@ read_spng_thumbnail(
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.
uint32_t *output = (uint32_t *) cairo_image_surface_get_data(surface);
cairo_surface_flush(surface);
struct spng_trns trns = {};
if (ihdr.color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA ||
ihdr.color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA ||
!spng_get_trns(ctx, &trns)) {
for (size_t i = size / sizeof *data; i--; ) {
const uint8_t *unit = (const uint8_t *) &data[i],
for (size_t i = size / sizeof *output; i--; ) {
uint8_t *unit = (uint8_t *) &data[i],
a = unit[3],
b = unit[2] * a / 255,
g = unit[1] * 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 {
for (size_t i = size / sizeof *data; i--; ) {
uint32_t rgba = g_ntohl(data[i]);
data[i] = rgba << 24 | rgba >> 8;
for (size_t i = size / sizeof *output; i--; ) {
uint8_t *unit = (uint8_t *) &data[i],
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));
fail_data:
if (!result)
cairo_surface_destroy(surface);
free(data);
fail:
spng_ctx_free(ctx);
fail_init:

View File

@ -181,10 +181,6 @@ 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