Use Little CMS's alpha premultiplication feature

And do a little cleanup.
This commit is contained in:
Přemysl Eric Janouch 2023-06-06 11:08:56 +02:00
parent 1c25cb411f
commit 6cc4ca1f44
Signed by: p
GPG Key ID: A0420B94F92B9493
2 changed files with 74 additions and 47 deletions

117
fiv-io.c
View File

@ -286,10 +286,15 @@ fiv_io_profile_free(FivIoProfile self)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// TODO(p): In general, try to use CAIRO_FORMAT_RGB30 or CAIRO_FORMAT_RGBA128F. // TODO(p): In general, try to use CAIRO_FORMAT_RGB30 or CAIRO_FORMAT_RGBA128F.
#define FIV_IO_LCMS2_ARGB32 \ #ifndef HAVE_LCMS2
#define FIV_IO_PROFILE_ARGB32 0
#define FIV_IO_PROFILE_4X16LE 0
#else
#define FIV_IO_PROFILE_ARGB32 \
(G_BYTE_ORDER == G_LITTLE_ENDIAN ? TYPE_BGRA_8 : TYPE_ARGB_8) (G_BYTE_ORDER == G_LITTLE_ENDIAN ? TYPE_BGRA_8 : TYPE_ARGB_8)
#define FIV_IO_LCMS2_4X16LE \ #define FIV_IO_PROFILE_4X16LE \
(G_BYTE_ORDER == G_LITTLE_ENDIAN ? TYPE_BGRA_16 : TYPE_BGRA_16_SE) (G_BYTE_ORDER == G_LITTLE_ENDIAN ? TYPE_BGRA_16 : TYPE_BGRA_16_SE)
#endif
// 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/BGRA/XRGB/BGRX. // ARGB/BGRA/XRGB/BGRX.
@ -332,7 +337,7 @@ fiv_io_profile_cmyk(
cmsHTRANSFORM transform = NULL; cmsHTRANSFORM transform = NULL;
if (source && target) { if (source && target) {
transform = cmsCreateTransform(source, TYPE_CMYK_8_REV, target, transform = cmsCreateTransform(source, TYPE_CMYK_8_REV, target,
FIV_IO_LCMS2_ARGB32, INTENT_PERCEPTUAL, 0); FIV_IO_PROFILE_ARGB32, INTENT_PERCEPTUAL, 0);
} }
if (transform) { if (transform) {
cmsDoTransform(transform, data, data, w * h); cmsDoTransform(transform, data, data, w * h);
@ -343,16 +348,20 @@ fiv_io_profile_cmyk(
trivial_cmyk_to_host_byte_order_argb(data, w * h); trivial_cmyk_to_host_byte_order_argb(data, w * h);
} }
static void static bool
fiv_io_profile_xrgb32_direct(unsigned char *data, int w, int h, fiv_io_profile_rgb_direct(unsigned char *data, int w, int h,
FivIoProfile source, FivIoProfile target) FivIoProfile source, FivIoProfile target,
uint32_t source_format, uint32_t target_format)
{ {
#ifndef HAVE_LCMS2 #ifndef HAVE_LCMS2
(void) data; (void) data;
(void) w; (void) w;
(void) h; (void) h;
(void) source; (void) source;
(void) source_format;
(void) target; (void) target;
(void) target_format;
return false;
#else #else
// TODO(p): We should make this optional. // TODO(p): We should make this optional.
cmsHPROFILE src_fallback = NULL; cmsHPROFILE src_fallback = NULL;
@ -361,8 +370,8 @@ fiv_io_profile_xrgb32_direct(unsigned char *data, int w, int h,
cmsHTRANSFORM transform = NULL; cmsHTRANSFORM transform = NULL;
if (source && target) { if (source && target) {
transform = cmsCreateTransform(source, FIV_IO_LCMS2_ARGB32, target, transform = cmsCreateTransform(
FIV_IO_LCMS2_ARGB32, INTENT_PERCEPTUAL, 0); source, source_format, target, target_format, INTENT_PERCEPTUAL, 0);
} }
if (transform) { if (transform) {
cmsDoTransform(transform, data, data, w * h); cmsDoTransform(transform, data, data, w * h);
@ -370,9 +379,18 @@ fiv_io_profile_xrgb32_direct(unsigned char *data, int w, int h,
} }
if (src_fallback) if (src_fallback)
cmsCloseProfile(src_fallback); cmsCloseProfile(src_fallback);
return transform != NULL;
#endif #endif
} }
static void
fiv_io_profile_xrgb32_direct(
unsigned char *data, int w, int h, FivIoProfile source, FivIoProfile target)
{
fiv_io_profile_rgb_direct(data, w, h, source, target,
FIV_IO_PROFILE_ARGB32, FIV_IO_PROFILE_ARGB32);
}
static void static void
fiv_io_profile_xrgb32( fiv_io_profile_xrgb32(
cairo_surface_t *surface, FivIoProfile source, FivIoProfile target) cairo_surface_t *surface, FivIoProfile source, FivIoProfile target)
@ -387,36 +405,15 @@ static void
fiv_io_profile_4x16le_direct( fiv_io_profile_4x16le_direct(
unsigned char *data, int w, int h, FivIoProfile source, FivIoProfile target) unsigned char *data, int w, int h, FivIoProfile source, FivIoProfile target)
{ {
#ifndef HAVE_LCMS2 fiv_io_profile_rgb_direct(data, w, h, source, target,
(void) data; FIV_IO_PROFILE_4X16LE, FIV_IO_PROFILE_4X16LE);
(void) w;
(void) h;
(void) source;
(void) target;
#else
// TODO(p): We should make this optional.
cmsHPROFILE src_fallback = NULL;
if (target && !source)
source = src_fallback = cmsCreate_sRGBProfile();
cmsHTRANSFORM transform = NULL;
if (source && target) {
transform = cmsCreateTransform(source, FIV_IO_LCMS2_4X16LE, target,
FIV_IO_LCMS2_4X16LE, INTENT_PERCEPTUAL, 0);
}
if (transform) {
cmsDoTransform(transform, data, data, w * h);
cmsDeleteTransform(transform);
}
if (src_fallback)
cmsCloseProfile(src_fallback);
#endif
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void static void
fiv_io_profile_xrgb32_page(cairo_surface_t *page, FivIoProfile target) fiv_io_profile_page(cairo_surface_t *page, FivIoProfile target,
void (*frame_cb) (cairo_surface_t *, FivIoProfile, FivIoProfile))
{ {
GBytes *bytes = NULL; GBytes *bytes = NULL;
FivIoProfile source = NULL; FivIoProfile source = NULL;
@ -426,12 +423,15 @@ fiv_io_profile_xrgb32_page(cairo_surface_t *page, FivIoProfile target)
// TODO(p): All animations need to be composited in a linear colour space. // TODO(p): All animations need to be composited in a linear colour space.
for (cairo_surface_t *frame = page; frame != NULL; for (cairo_surface_t *frame = page; frame != NULL;
frame = cairo_surface_get_user_data(frame, &fiv_io_key_frame_next)) frame = cairo_surface_get_user_data(frame, &fiv_io_key_frame_next))
fiv_io_profile_xrgb32(frame, source, target); frame_cb(frame, source, target);
if (source) if (source)
fiv_io_profile_free(source); fiv_io_profile_free(source);
} }
#define fiv_io_profile_xrgb32_page(page, target) \
fiv_io_profile_page((page), (target), fiv_io_profile_xrgb32)
// TODO(p): Offer better integration, upgrade the bit depth if appropriate. // TODO(p): Offer better integration, upgrade the bit depth if appropriate.
static cairo_surface_t * static cairo_surface_t *
fiv_io_profile_finalize(cairo_surface_t *image, FivIoProfile target) fiv_io_profile_finalize(cairo_surface_t *image, FivIoProfile target)
@ -451,6 +451,8 @@ fiv_io_profile_finalize(cairo_surface_t *image, FivIoProfile target)
return image; return image;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void static void
fiv_io_premultiply_argb32(cairo_surface_t *surface) fiv_io_premultiply_argb32(cairo_surface_t *surface)
{ {
@ -473,14 +475,45 @@ fiv_io_premultiply_argb32(cairo_surface_t *surface)
} }
} }
#if defined HAVE_LCMS2 && LCMS_VERSION >= 2130
#define FIV_IO_PROFILE_ARGB32_PREMUL \
(G_BYTE_ORDER == G_LITTLE_ENDIAN ? TYPE_BGRA_8_PREMUL : TYPE_ARGB_8_PREMUL)
static void static void
fiv_io_premultiply_argb32_page(cairo_surface_t *page) fiv_io_profile_argb32_premultiply(cairo_surface_t *surface,
FivIoProfile source, FivIoProfile target)
{ {
int w = cairo_image_surface_get_width(surface);
int h = cairo_image_surface_get_height(surface);
unsigned char *data = cairo_image_surface_get_data(surface);
if (cairo_image_surface_get_format(surface) != CAIRO_FORMAT_ARGB32) {
fiv_io_profile_xrgb32_direct(data, w, h, source, target);
} else if (!fiv_io_profile_rgb_direct(data, w, h, source, target,
FIV_IO_PROFILE_ARGB32, FIV_IO_PROFILE_ARGB32_PREMUL)) {
g_debug("failed to create a premultiplying transform");
fiv_io_premultiply_argb32(surface);
}
}
#define fiv_io_profile_argb32_premultiply_page(page, target) \
fiv_io_profile_page((page), (target), fiv_io_profile_argb32_premultiply)
#else // ! HAVE_LCMS2 || LCMS_VERSION < 2130
static void
fiv_io_profile_argb32_premultiply_page(
cairo_surface_t *page, FivIoProfile target)
{
fiv_io_profile_xrgb32_page(page, target);
for (cairo_surface_t *frame = page; frame != NULL; for (cairo_surface_t *frame = page; frame != NULL;
frame = cairo_surface_get_user_data(frame, &fiv_io_key_frame_next)) frame = cairo_surface_get_user_data(frame, &fiv_io_key_frame_next))
fiv_io_premultiply_argb32(frame); fiv_io_premultiply_argb32(frame);
} }
#endif // ! HAVE_LCMS2 || LCMS_VERSION < 2130
// --- Wuffs ------------------------------------------------------------------- // --- Wuffs -------------------------------------------------------------------
static bool static bool
@ -1831,10 +1864,8 @@ open_libwebp(
} }
WebPDemuxDelete(demux); WebPDemuxDelete(demux);
if (ctx->screen_profile) { if (ctx->screen_profile)
fiv_io_profile_xrgb32_page(result, ctx->screen_profile); fiv_io_profile_argb32_premultiply_page(result, ctx->screen_profile);
fiv_io_premultiply_argb32_page(result);
}
fail: fail:
WebPFreeDecBuffer(&config.output); WebPFreeDecBuffer(&config.output);
@ -3102,12 +3133,10 @@ open_gdkpixbuf(
} }
g_object_unref(pixbuf); g_object_unref(pixbuf);
if (custom_argb32) { if (custom_argb32)
fiv_io_profile_xrgb32_page(surface, ctx->screen_profile); fiv_io_profile_argb32_premultiply_page(surface, ctx->screen_profile);
fiv_io_premultiply_argb32_page(surface); else
} else {
surface = fiv_io_profile_finalize(surface, ctx->screen_profile); surface = fiv_io_profile_finalize(surface, ctx->screen_profile);
}
return surface; return surface;
} }

View File

@ -24,9 +24,7 @@
// --- Colour management ------------------------------------------------------- // --- Colour management -------------------------------------------------------
// TODO(p): Make it possible to use Skia's skcms, // TODO(p): Make it also possible to use Skia's skcms.
// which also supports premultiplied alpha.
// NOTE: Little CMS 2.13 already supports premultiplied alpha, too.
typedef void *FivIoProfile; typedef void *FivIoProfile;
FivIoProfile fiv_io_profile_new(const void *data, size_t len); FivIoProfile fiv_io_profile_new(const void *data, size_t len);
FivIoProfile fiv_io_profile_new_sRGB(void); FivIoProfile fiv_io_profile_new_sRGB(void);