Compare commits

...

20 Commits

Author SHA1 Message Date
Matthias Clasen
d4e536ebc6 image-tool: build with AVIF_DEBUG for now
This makes it easier to find out whats going on while the avif
loader is still frequently touched.
2024-08-10 10:09:56 -04:00
Matthias Clasen
00aec81d79 image tool: Load and save avif using the loader
We don't want to use the gdk-pixbuf loader, since it doesn not
produce dmabufs, and has no idea about color states and >8bit
formats.

With this commit, the image tool will support avif in both
the relabel and the convert commands (provided we are building
with avif support).
2024-08-10 10:09:56 -04:00
Matthias Clasen
d536d4ab78 image tool: Better color state support
Show custom color states with their cicp tuple, and recognize
the name "jpeg".
2024-08-10 10:09:56 -04:00
Matthias Clasen
6d5664c4e3 Add yuv roundtrip tests
These load avif images in various formats to produce dmabuf or memory
textures, then download the data to compare it with the original png.

For now, the images are single-color, to avoid extra noise due to
reconstruction from subsampled formats.
2024-08-10 10:09:56 -04:00
Matthias Clasen
8343ba9ce3 Support avif in the render node parser
When saving, use avif if it is available and the texture is yuv.

When loading, look at the mimetype to decide whether to use the
avif loader.
2024-08-10 09:16:57 -04:00
Matthias Clasen
c68a189005 Add an avif loder
Add a loader for avif images. It uses libavif and produces dmabufs
when /dev/udmabuf is available. Otherwise it produces YUVA memory
textures.

For saving, currently only YUV444 is supported. It would be nice
to just save dmabufs as-is, but that requires more work.

This is meant to facilitate testing of our yuv support, and
therefore, it is not exposed via the public texture loading and
saving apis. It is disabled by default and should *not* be enabled
in distro builds.

avif: Make it build outside of libgtk

We want to use this code in the image tool, and we don't want to
link it statically.

avif loader fixes
2024-08-10 09:16:57 -04:00
Matthias Clasen
567de6317d gpu: When importing dmabufs, adjust colorstate
We need to account for the yuv->rgb conversion that may happen
during import. Use the new color state adjustment helper for this.
2024-08-10 09:16:57 -04:00
Matthias Clasen
502a7f201b gpu: Add a helper to adjust color states
Add a function that takes an image and a colorstate, and adjusts
the colorstate according to the image flags. This is meant to handle
cases where the upload changes the effective color state of the data,
such as GL_SRGB or importing yuv as external texture.

Note: this function returns a reference.
2024-08-10 09:16:57 -04:00
Matthias Clasen
0d33aafe9a gsk: Add image flags for yuv
We want to separate the yuv->rgb conversion from the texture
upload, and if we do so we need to keep a record of whether it
happened or not.

Also add a mask that collects all the yuv-related flags for
convenient checking.
2024-08-10 09:16:57 -04:00
Matthias Clasen
50e7f02fc4 egl: Use correct yuv details for importing
Make the dmabuf import api take color space and range hints as
ints, and update all callers to pass them.

The callers use the new dmabuf egl yuv hints helper api to get
this information from the dmabuf + colorstate. Note that the old
gl renderer is not handling color states carefully and may not
get dmabuf import right wrt to color states.
2024-08-10 09:16:57 -04:00
Matthias Clasen
83ba577fa8 Add a helper for egl yuv hints 2024-08-10 09:16:57 -04:00
Matthias Clasen
c53f791cb3 gst: Set matrix coefficients
This is needed to use our color conversion machinery successfully.

We need some heuristics to determine what coefficients and range
to use when gstreamer gives us an 'unspecified' value. For that,
we look at the fourcc.
2024-08-10 09:16:57 -04:00
Matthias Clasen
3445d2c12a jpeg: Save YCrCb data without conversion
If the texture we are saving is in the right colorstate, just use
the data without converting it to srgb first, only for libjpeg to
convert it back.
2024-08-10 09:16:57 -04:00
Matthias Clasen
22c119094c jpeg: Load YCbCr data without conversion
We can represent this data with a colorstate now, and feed it into
our color conversion machinery, instead of relying on libjpeg to
do the yuv->rgb conversion.
2024-08-10 09:16:57 -04:00
Matthias Clasen
839095d968 dmabuf: Fix P010 download
P010 has zeros in the 6 low bits. So what we want to do here is
to shift the top bits down and add them. This is necessary to
make the 'white' value in P010 (which is 1111111111000000) match
the highest value of 0xffff.
2024-08-10 09:16:52 -04:00
Matthias Clasen
1455933d61 dmabuf: Drop handrolled yuv conversions
Make the download function just unpack the dmabuf format, and
leave the color state conversion to the conversion call that
we do afterwards. This relies on dmabuf textures having the
correct color state set.
2024-08-10 09:11:06 -04:00
Matthias Clasen
406f563559 dmabuf: Set a proper color state for yuv
When we have to guess the color state because none was explicitly
set, take yuv-ness of the dmabuf fourcc into account.
2024-08-10 09:11:06 -04:00
Matthias Clasen
1a188e1171 colorstate: Add yuv support
Add support for some of the yuv-related cicp tuples.

Concretely, this adds bt601, bt709 and bt2020, both in their
narrow and full-range variants.
2024-08-10 09:11:05 -04:00
Matthias Clasen
c7780d7528 gsk: Make gsk_gpu_cache_lookup_tile return a ref
Make this function return a reference to the cached color state.
This will integrate better in the caller where we may have to
create a new color state, and thus need a reference. And since
the function returns a reference to the image as well, it seems
more consistent to return a reference to the color state too.
2024-08-10 08:57:41 -04:00
Matthias Clasen
05e7b00453 gsk: Change refcounting of color states
gsk_gpu_lookup_texture may need to return a newly constructed
color state in the future, so make it return a reference, and
update all callers.
2024-08-10 08:57:41 -04:00
81 changed files with 2164 additions and 247 deletions

View File

@@ -265,6 +265,13 @@ support in the file chooser.
This option controls whether GTK should use colord for color
calibration support in the cups print backend.
## `avif`
This option controls whether GTK will use libavif for loading
textures from AVIF image files. Note that the purpose of this option
is to facilitate development of GTK. The support for this image format
is not guaranteed and may go away at any time.
### `documentation`, `man-pages` and `screenshots`
The *gi-docgen* package is used to generate the reference documentation

View File

@@ -208,7 +208,10 @@ gdk_cicp_params_class_init (GdkCicpParamsClass *klass)
* Supported values:
*
* - 0: RGB
* - 1: BT.709
* - 2: unspecified
* - 5,6: BT.601
* - 9: BT.2020
*
* Since: 4.16
*/

View File

@@ -222,3 +222,39 @@ static const float srgb_to_rec2020[9] = {
0.069108, 0.919519, 0.011360,
0.016394, 0.088011, 0.895380,
};
static const float rgb_to_bt601[9] = {
0.299000, 0.587000, 0.114000,
-0.168736, -0.331264, 0.500000,
0.500000, -0.418688, -0.081312,
};
static const float bt601_to_rgb[9] = {
1.000000, 0.000000, 1.402000,
1.000000, -0.344136, -0.714136,
1.000000, 1.772000, 0.000000,
};
static const float rgb_to_bt709[9] = {
0.212600, 0.715200, 0.072200,
-0.114572, -0.385428, 0.500000,
0.500000, -0.454153, -0.045847,
};
static const float bt709_to_rgb[9] = {
1.000000, 0.000000, 1.574800,
1.000000, -0.187324, -0.468124,
1.000000, 1.855600, -0.000000,
};
static const float rgb_to_bt2020[9] = {
0.262700, 0.678000, 0.059300,
-0.139630, -0.360370, 0.500000,
0.500000, -0.459786, -0.040214,
};
static const float bt2020_to_rgb[9] = {
1.000000, -0.000000, 1.474600,
1.000000, -0.164553, -0.571353,
1.000000, 1.881400, -0.000000,
};

View File

@@ -18,6 +18,7 @@
#include "config.h"
#define GDK_COLOR_STATE_IMPL
#include "gdkcolorstateprivate.h"
#include <math.h>
@@ -411,7 +412,7 @@ GdkDefaultColorState gdk_default_color_states[] = {
},
};
/* }}} */
/* }}} */
/* {{{ Cicp implementation */
typedef struct _GdkCicpColorState GdkCicpColorState;
@@ -431,25 +432,128 @@ struct _GdkCicpColorState
float *from_srgb;
float *from_rec2020;
const float *from_yuv;
const float *to_yuv;
GdkCicp cicp;
};
/* {{{ Conversion functions */
#define cicp ((GdkCicpColorState *)self)
#define TRANSFORM_FROM_CICP(name, matrix, oetf) \
static void \
name (GdkColorState *color_state, \
float (*values)[4], \
gsize n_values) \
{ \
GdkCicpColorState *self = (GdkCicpColorState *) color_state; \
\
for (gsize i = 0; i < n_values; i++) \
{ \
if (self->cicp.range == GDK_CICP_RANGE_NARROW) \
{ \
values[i][0] = CLAMP ((values[i][0] - 16.0/255.0) * 255.0 / 219.0, -10, 10); \
values[i][1] = CLAMP ((values[i][1] - 16.0/255.0) * 255.0 / 224.0, -10, 10); \
values[i][2] = CLAMP ((values[i][2] - 16.0/255.0) * 255.0 / 224.0, -10, 10); \
} \
if (self->from_yuv) \
{ \
float res[3]; \
values[i][1] -= 0.5; \
values[i][2] -= 0.5; \
res[0] = self->from_yuv[0] * values[i][0] + self->from_yuv[1] * values[i][1] + self->from_yuv[2] * values[i][2]; \
res[1] = self->from_yuv[3] * values[i][0] + self->from_yuv[4] * values[i][1] + self->from_yuv[5] * values[i][2]; \
res[2] = self->from_yuv[6] * values[i][0] + self->from_yuv[7] * values[i][1] + self->from_yuv[8] * values[i][2]; \
values[i][0] = res[0]; \
values[i][1] = res[1]; \
values[i][2] = res[2]; \
} \
if (self->eotf != NONE) \
{ \
values[i][0] = self->eotf (values[i][0]); \
values[i][1] = self->eotf (values[i][1]); \
values[i][2] = self->eotf (values[i][2]); \
} \
if (self->matrix != IDENTITY) \
{ \
float res[3]; \
res[0] = self->matrix[0] * values[i][0] + self->matrix[1] * values[i][1] + self->matrix[2] * values[i][2]; \
res[1] = self->matrix[3] * values[i][0] + self->matrix[4] * values[i][1] + self->matrix[5] * values[i][2]; \
res[2] = self->matrix[6] * values[i][0] + self->matrix[7] * values[i][1] + self->matrix[8] * values[i][2]; \
values[i][0] = res[0]; \
values[i][1] = res[1]; \
values[i][2] = res[2]; \
} \
if (oetf != NONE) \
{ \
values[i][0] = oetf (values[i][0]); \
values[i][1] = oetf (values[i][1]); \
values[i][2] = oetf (values[i][2]); \
} \
} \
}
TRANSFORM(gdk_cicp_to_srgb, cicp->eotf, cicp->to_srgb, srgb_oetf)
TRANSFORM(gdk_cicp_to_srgb_linear, cicp->eotf, cicp->to_srgb, NONE)
TRANSFORM(gdk_cicp_to_rec2100_pq, cicp->eotf, cicp->to_rec2020, pq_oetf)
TRANSFORM(gdk_cicp_to_rec2100_linear, cicp->eotf, cicp->to_rec2020, NONE)
TRANSFORM(gdk_cicp_from_srgb, srgb_eotf, cicp->from_srgb, cicp->oetf)
TRANSFORM(gdk_cicp_from_srgb_linear, NONE, cicp->from_srgb, cicp->oetf)
TRANSFORM(gdk_cicp_from_rec2100_pq, pq_eotf, cicp->from_rec2020, cicp->oetf)
TRANSFORM(gdk_cicp_from_rec2100_linear, NONE, cicp->from_rec2020, cicp->oetf)
#define TRANSFORM_TO_CICP(name, eotf, matrix) \
static void \
name (GdkColorState *color_state, \
float (*values)[4], \
gsize n_values) \
{ \
GdkCicpColorState *self = (GdkCicpColorState *) color_state; \
\
for (gsize i = 0; i < n_values; i++) \
{ \
if (eotf != NONE) \
{ \
values[i][0] = eotf (values[i][0]); \
values[i][1] = eotf (values[i][1]); \
values[i][2] = eotf (values[i][2]); \
} \
if (self->matrix != IDENTITY) \
{ \
float res[3]; \
res[0] = self->matrix[0] * values[i][0] + self->matrix[1] * values[i][1] + self->matrix[2] * values[i][2]; \
res[1] = self->matrix[3] * values[i][0] + self->matrix[4] * values[i][1] + self->matrix[5] * values[i][2]; \
res[2] = self->matrix[6] * values[i][0] + self->matrix[7] * values[i][1] + self->matrix[8] * values[i][2]; \
values[i][0] = res[0]; \
values[i][1] = res[1]; \
values[i][2] = res[2]; \
} \
if (self->oetf != NONE) \
{ \
values[i][0] = self->oetf (values[i][0]); \
values[i][1] = self->oetf (values[i][1]); \
values[i][2] = self->oetf (values[i][2]); \
} \
if (self->to_yuv) \
{ \
float res[3]; \
res[0] = self->to_yuv[0] * values[i][0] + self->to_yuv[1] * values[i][1] + self->to_yuv[2] * values[i][2]; \
res[1] = self->to_yuv[3] * values[i][0] + self->to_yuv[4] * values[i][1] + self->to_yuv[5] * values[i][2]; \
res[2] = self->to_yuv[6] * values[i][0] + self->to_yuv[7] * values[i][1] + self->to_yuv[8] * values[i][2]; \
values[i][0] = res[0]; \
values[i][1] = res[1] + 0.5; \
values[i][2] = res[2] + 0.5; \
} \
if (self->cicp.range == GDK_CICP_RANGE_NARROW) \
{ \
values[i][0] = values[i][0] * 219.0 / 255.0 + 16.0 / 255.0; \
values[i][1] = values[i][1] * 224.0 / 255.0 + 16.0 / 255.0; \
values[i][2] = values[i][2] * 224.0 / 255.0 + 16.0 / 255.0; \
} \
} \
}
#undef cicp
TRANSFORM_FROM_CICP(gdk_cicp_to_srgb, to_srgb, srgb_oetf)
TRANSFORM_FROM_CICP(gdk_cicp_to_srgb_linear, to_srgb, NONE)
TRANSFORM_FROM_CICP(gdk_cicp_to_rec2100_pq, to_rec2020, pq_oetf)
TRANSFORM_FROM_CICP(gdk_cicp_to_rec2100_linear, to_rec2020, NONE)
TRANSFORM_TO_CICP(gdk_cicp_from_srgb, srgb_eotf, from_srgb)
TRANSFORM_TO_CICP(gdk_cicp_from_srgb_linear, NONE, from_srgb)
TRANSFORM_TO_CICP(gdk_cicp_from_rec2100_pq, pq_eotf, from_rec2020)
TRANSFORM_TO_CICP(gdk_cicp_from_rec2100_linear, NONE, from_rec2020)
/* }}} */
/* }}} */
/* {{{ Vfuncs */
@@ -555,7 +659,7 @@ gdk_cicp_color_state_get_cicp (GdkColorState *color_state)
return &self->cicp;
}
/* }}} */
/* }}} */
static const
GdkColorStateClass GDK_CICP_COLOR_STATE_CLASS = {
@@ -568,6 +672,46 @@ GdkColorStateClass GDK_CICP_COLOR_STATE_CLASS = {
.get_cicp = gdk_cicp_color_state_get_cicp,
};
GdkCicpColorState gdk_color_state_bt601_narrow = {
.parent = {
.klass = &GDK_CICP_COLOR_STATE_CLASS,
.ref_count = 1,
.depth = GDK_MEMORY_FLOAT16,
.rendering_color_state = GDK_COLOR_STATE_REC2100_LINEAR,
},
.name = "cicp-1/13/6/0",
.no_srgb = NULL,
.cicp = { 1, 13, 6, 0 },
.eotf = srgb_eotf,
.oetf = srgb_oetf,
.to_yuv = rgb_to_bt601,
.from_yuv = bt601_to_rgb,
.to_srgb = IDENTITY,
.to_rec2020 = (float *) srgb_to_rec2020,
.from_srgb = IDENTITY,
.from_rec2020 = (float *) rec2020_to_srgb,
};
GdkCicpColorState gdk_color_state_bt601_full = {
.parent = {
.klass = &GDK_CICP_COLOR_STATE_CLASS,
.ref_count = 1,
.depth = GDK_MEMORY_FLOAT16,
.rendering_color_state = GDK_COLOR_STATE_REC2100_LINEAR,
},
.name = "cicp-1/13/6/1",
.no_srgb = NULL,
.cicp = { 1, 13, 6, 1 },
.eotf = srgb_eotf,
.oetf = srgb_oetf,
.to_yuv = rgb_to_bt601,
.from_yuv = bt601_to_rgb,
.to_srgb = IDENTITY,
.to_rec2020 = (float *) srgb_to_rec2020,
.from_srgb = IDENTITY,
.from_rec2020 = (float *) rec2020_to_srgb,
};
static inline float *
multiply (float res[9],
const float m1[9],
@@ -592,14 +736,8 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp,
GdkTransferFunc oetf;
gconstpointer to_xyz;
gconstpointer from_xyz;
if (cicp->range == GDK_CICP_RANGE_NARROW || cicp->matrix_coefficients != 0)
{
g_set_error (error,
G_IO_ERROR, G_IO_ERROR_FAILED,
_("cicp: Narrow range or YUV not supported"));
return NULL;
}
gconstpointer to_yuv = NULL;
gconstpointer from_yuv = NULL;
if (cicp->color_primaries == 2 ||
cicp->transfer_function == 2 ||
@@ -613,10 +751,16 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp,
for (guint i = 0; i < GDK_COLOR_STATE_N_IDS; i++)
{
if (gdk_cicp_equivalent (cicp, &gdk_default_color_states[i].cicp))
if (gdk_cicp_equal (cicp, &gdk_default_color_states[i].cicp))
return (GdkColorState *) &gdk_default_color_states[i];
}
if (gdk_cicp_equal (cicp, &gdk_color_state_bt601_narrow.cicp))
return gdk_color_state_ref ((GdkColorState *) &gdk_color_state_bt601_narrow);
if (gdk_cicp_equal (cicp, &gdk_color_state_bt601_full.cicp))
return gdk_color_state_ref ((GdkColorState *) &gdk_color_state_bt601_full);
switch (cicp->transfer_function)
{
case 1:
@@ -669,6 +813,7 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp,
from_xyz = xyz_to_pal;
break;
case 6:
case 7:
to_xyz = ntsc_to_xyz;
from_xyz = xyz_to_ntsc;
break;
@@ -688,6 +833,34 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp,
return NULL;
}
switch (cicp->matrix_coefficients)
{
case 0:
to_yuv = IDENTITY;
from_yuv = IDENTITY;
break;
case 1:
to_yuv = rgb_to_bt709;
from_yuv = bt709_to_rgb;
break;
case 5:
case 6:
to_yuv = rgb_to_bt601;
from_yuv = bt601_to_rgb;
break;
case 9:
to_yuv = rgb_to_bt2020;
from_yuv = bt2020_to_rgb;
break;
default:
g_set_error (error,
G_IO_ERROR, G_IO_ERROR_FAILED,
_("cicp: Matrix coefficients %u, %s not supported"),
cicp->matrix_coefficients,
cicp->range == GDK_CICP_RANGE_NARROW ? "narrow" : "full");
return NULL;
}
self = g_new0 (GdkCicpColorState, 1);
self->parent.klass = &GDK_CICP_COLOR_STATE_CLASS;
@@ -700,6 +873,9 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp,
memcpy (&self->cicp, cicp, sizeof (GdkCicp));
self->to_yuv = to_yuv;
self->from_yuv = from_yuv;
self->eotf = eotf;
self->oetf = oetf;

View File

@@ -211,3 +211,10 @@ gdk_color_state_from_rgba (GdkColorState *self,
out_color);
}
#ifndef GDK_COLOR_STATE_IMPL
extern GdkColorState gdk_color_state_bt601_narrow;
extern GdkColorState gdk_color_state_bt601_full;
#endif
#define GDK_COLOR_STATE_YUV ((GdkColorState *) &gdk_color_state_bt601_narrow)
#define GDK_COLOR_STATE_JPEG ((GdkColorState *) &gdk_color_state_bt601_full)

View File

@@ -24,6 +24,7 @@
#include "gdkdmabuffourccprivate.h"
#include "gdkdmabuftextureprivate.h"
#include "gdkmemoryformatprivate.h"
#include "gdkcolorstate.h"
#ifdef HAVE_DMABUF
#include <sys/mman.h>
@@ -133,49 +134,6 @@ download_memcpy_3_1 (guchar *dst_data,
}
}
typedef struct _YUVCoefficients YUVCoefficients;
struct _YUVCoefficients
{
int v_to_r;
int u_to_g;
int v_to_g;
int u_to_b;
};
/* multiplied by 65536 */
static const YUVCoefficients itu601_narrow = { 104597, -25675, -53279, 132201 };
//static const YUVCoefficients itu601_wide = { 74711, -25864, -38050, 133176 };
static inline void
get_uv_values (const YUVCoefficients *coeffs,
guint8 u,
guint8 v,
int *out_r,
int *out_g,
int *out_b)
{
int u2 = (int) u - 127;
int v2 = (int) v - 127;
*out_r = coeffs->v_to_r * v2;
*out_g = coeffs->u_to_g * u2 + coeffs->v_to_g * v2;
*out_b = coeffs->u_to_b * u2;
}
static inline void
set_rgb_values (guint8 rgb[3],
guint8 y,
int r,
int g,
int b)
{
int y2 = y * 65536;
rgb[0] = CLAMP ((y2 + r) >> 16, 0, 255);
rgb[1] = CLAMP ((y2 + g) >> 16, 0, 255);
rgb[2] = CLAMP ((y2 + b) >> 16, 0, 255);
}
static void
download_nv12 (guchar *dst_data,
gsize dst_stride,
@@ -226,14 +184,21 @@ download_nv12 (guchar *dst_data,
{
for (x = 0; x < width; x += X_SUB)
{
int r, g, b;
int u_, v_;
gsize xs, ys;
get_uv_values (&itu601_narrow, uv_data[x / X_SUB * 2 + U], uv_data[x / X_SUB * 2 + V], &r, &g, &b);
u_ = uv_data[x / X_SUB * 2 + U];
v_ = uv_data[x / X_SUB * 2 + V];
for (ys = 0; ys < Y_SUB && y + ys < height; ys++)
for (xs = 0; xs < X_SUB && x + xs < width; xs++)
set_rgb_values (&dst_data[3 * (x + xs) + dst_stride * ys], y_data[x + xs + y_stride * ys], r, g, b);
{
guint8 *rgb = &dst_data[3 * (x + xs) + dst_stride * ys];
rgb[0] = y_data[x + xs + y_stride * ys];
rgb[1] = u_;
rgb[2] = v_;
}
}
dst_data += Y_SUB * dst_stride;
y_data += Y_SUB * y_stride;
@@ -241,35 +206,6 @@ download_nv12 (guchar *dst_data,
}
}
static inline void
get_uv_values16 (const YUVCoefficients *coeffs,
guint16 u,
guint16 v,
gint64 *out_r,
gint64 *out_g,
gint64 *out_b)
{
gint64 u2 = (gint64) u - 32767;
gint64 v2 = (gint64) v - 32767;
*out_r = coeffs->v_to_r * v2;
*out_g = coeffs->u_to_g * u2 + coeffs->v_to_g * v2;
*out_b = coeffs->u_to_b * u2;
}
static inline void
set_rgb_values16 (guint16 rgb[3],
guint16 y,
gint64 r,
gint64 g,
gint64 b)
{
gint64 y2 = (gint64) y * 65536;
rgb[0] = CLAMP ((y2 + r) >> 16, 0, 65535);
rgb[1] = CLAMP ((y2 + g) >> 16, 0, 65535);
rgb[2] = CLAMP ((y2 + b) >> 16, 0, 65535);
}
static void
download_p010 (guchar *dst,
gsize dst_stride,
@@ -284,7 +220,7 @@ download_p010 (guchar *dst,
guint16 *dst_data;
gsize x, y, y_stride, uv_stride;
gsize U, V, X_SUB, Y_SUB;
guint16 SIZE, MASK;
guint16 SIZE;
switch (dmabuf->fourcc)
{
@@ -304,7 +240,6 @@ download_p010 (guchar *dst,
g_assert_not_reached ();
return;
}
MASK = 0xFFFF << (16 - SIZE);
y_stride = dmabuf->planes[0].stride / 2;
y_data = (const guint16 *) (src_data[0] + dmabuf->planes[0].offset);
@@ -319,22 +254,24 @@ download_p010 (guchar *dst,
{
for (x = 0; x < width; x += X_SUB)
{
gint64 r, g, b;
gsize xs, ys;
guint16 u, v;
guint16 u_, v_;
u = uv_data[x / X_SUB * 2 + U];
u = (u & MASK) | (u >> SIZE);
v = uv_data[x / X_SUB * 2 + V];
v = (v & MASK) | (v >> SIZE);
get_uv_values16 (&itu601_narrow, u, v, &r, &g, &b);
u_ = uv_data[x / X_SUB * 2 + U];
u_ = u_ | (u_ >> SIZE);
v_ = uv_data[x / X_SUB * 2 + V];
v_ = v_ | (v_ >> SIZE);
for (ys = 0; ys < Y_SUB && y + ys < height; ys++)
for (xs = 0; xs < X_SUB && x + xs < width; xs++)
{
guint16 *rgb = &dst_data[3 * (x + xs) + dst_stride * ys];
guint16 y_ = y_data[x + xs + y_stride * ys];
y_ = (y_ & MASK) | (y_ >> SIZE);
set_rgb_values16 (&dst_data[3 * (x + xs) + dst_stride * ys], y_, r, g, b);
y_ = y_ | (y_ >> SIZE);
rgb[0] = y_;
rgb[1] = u_;
rgb[2] = v_;
}
}
dst_data += Y_SUB * dst_stride;
@@ -408,14 +345,21 @@ download_yuv_3 (guchar *dst_data,
{
for (x = 0; x < width; x += X_SUB)
{
int r, g, b;
int u_, v_;
gsize xs, ys;
get_uv_values (&itu601_narrow, u_data[x / X_SUB], v_data[x / X_SUB], &r, &g, &b);
u_ = u_data[x / X_SUB];
v_ = v_data[x / X_SUB];
for (ys = 0; ys < Y_SUB && y + ys < height; ys++)
for (xs = 0; xs < X_SUB && x + xs < width; xs++)
set_rgb_values (&dst_data[3 * (x + xs) + dst_stride * ys], y_data[x + xs + y_stride * ys], r, g, b);
{
guint8 *rgb = &dst_data[3 * (x + xs) + dst_stride * ys];
rgb[0] = y_data[x + xs + y_stride * ys];
rgb[1] = u_;
rgb[2] = v_;
}
}
dst_data += Y_SUB * dst_stride;
y_data += Y_SUB * y_stride;
@@ -465,12 +409,23 @@ download_yuyv (guchar *dst_data,
{
for (x = 0; x < width; x += 2)
{
int r, g, b;
guint8 *rgb;
int u_, v_;
get_uv_values (&itu601_narrow, src_data[2 * x + U], src_data[2 * x + V], &r, &g, &b);
set_rgb_values (&dst_data[3 * x], src_data[2 * x + Y1], r, g, b);
u_ = src_data[2 * x + U];
v_ = src_data[2 * x + V];
rgb = &dst_data[3 * x];
rgb[0] = src_data[2 * x + Y1];
rgb[1] = u_;
rgb[2] = v_;
if (x + 1 < width)
set_rgb_values (&dst_data[3 * (x + 1)], src_data[2 * x + Y2], r, g, b);
{
rgb = &dst_data[3 * (x + 1)];
rgb[0] = src_data[2 * x + Y2];
rgb[1] = u_;
rgb[2] = v_;
}
}
dst_data += dst_stride;
src_data += src_stride;
@@ -2139,14 +2094,14 @@ gdk_dmabuf_do_download_mmap (GdkTexture *texture,
needs_unmap[i] = TRUE;
}
info->download (data,
stride,
gdk_texture_get_format (texture),
gdk_texture_get_width (texture),
gdk_texture_get_height (texture),
dmabuf,
src_data,
sizes);
info->download (data,
stride,
gdk_texture_get_format (texture),
gdk_texture_get_width (texture),
gdk_texture_get_height (texture),
dmabuf,
src_data,
sizes);
out:
for (i = 0; i < dmabuf->n_planes; i++)

View File

@@ -209,12 +209,15 @@ gdk_dmabuf_egl_create_image (GdkDisplay *display,
int width,
int height,
const GdkDmabuf *dmabuf,
int color_space_hint,
int range_hint,
int target)
{
EGLDisplay egl_display = gdk_display_get_egl_display (display);
EGLint attribs[64];
int i;
EGLImage image;
gboolean is_yuv;
g_return_val_if_fail (width > 0, 0);
g_return_val_if_fail (height > 0, 0);
@@ -228,6 +231,25 @@ gdk_dmabuf_egl_create_image (GdkDisplay *display,
return EGL_NO_IMAGE;
}
if (gdk_dmabuf_fourcc_is_yuv (dmabuf->fourcc, &is_yuv) && is_yuv)
{
if (color_space_hint == 0 || range_hint == 0)
{
GDK_DISPLAY_DEBUG (display, DMABUF,
"Can't import yuv dmabuf into GL without color space hints");
return EGL_NO_IMAGE;
}
}
else
{
if (color_space_hint != 0 || range_hint != 0)
{
GDK_DISPLAY_DEBUG (display, DMABUF,
"Can't import non-yuv dmabuf into GL with color space hints");
return EGL_NO_IMAGE;
}
}
GDK_DISPLAY_DEBUG (display, DMABUF,
"Importing dmabuf (format: %.4s:%#" G_GINT64_MODIFIER "x, planes: %u) into GL",
(char *) &dmabuf->fourcc, dmabuf->modifier, dmabuf->n_planes);
@@ -241,10 +263,16 @@ gdk_dmabuf_egl_create_image (GdkDisplay *display,
attribs[i++] = height;
attribs[i++] = EGL_LINUX_DRM_FOURCC_EXT;
attribs[i++] = dmabuf->fourcc;
attribs[i++] = EGL_YUV_COLOR_SPACE_HINT_EXT;
attribs[i++] = EGL_ITU_REC601_EXT;
attribs[i++] = EGL_SAMPLE_RANGE_HINT_EXT;
attribs[i++] = EGL_YUV_NARROW_RANGE_EXT;
if (color_space_hint != 0)
{
attribs[i++] = EGL_YUV_COLOR_SPACE_HINT_EXT;
attribs[i++] = color_space_hint;
}
if (range_hint != 0)
{
attribs[i++] = EGL_SAMPLE_RANGE_HINT_EXT;
attribs[i++] = range_hint;
}
#define ADD_PLANE(plane) \
{ \
@@ -289,4 +317,48 @@ gdk_dmabuf_egl_create_image (GdkDisplay *display,
return image;
}
void
gdk_dmabuf_get_egl_yuv_hints (const GdkDmabuf *dmabuf,
GdkColorState *color_state,
int *color_space_hint,
int *range_hint)
{
gboolean is_yuv;
const GdkCicp *cicp;
cicp = gdk_color_state_get_cicp (color_state);
if (cicp &&
gdk_dmabuf_fourcc_is_yuv (dmabuf->fourcc, &is_yuv) && is_yuv)
{
if (cicp->range == GDK_CICP_RANGE_NARROW)
*range_hint = EGL_YUV_NARROW_RANGE_EXT;
else
*range_hint = EGL_YUV_FULL_RANGE_EXT;
switch (cicp->matrix_coefficients)
{
case 1:
*color_space_hint = EGL_ITU_REC709_EXT;
break;
case 5:
case 6:
*color_space_hint = EGL_ITU_REC601_EXT;
break;
case 9:
*color_space_hint = EGL_ITU_REC2020_EXT;
break;
default:
*color_space_hint = 0;
*range_hint = 0;
break;
}
}
else
{
*color_space_hint = 0;
*range_hint = 0;
}
}
#endif /* HAVE_DMABUF && HAVE_EGL */

View File

@@ -13,6 +13,13 @@ EGLImage gdk_dmabuf_egl_create_image (GdkDisplay
int width,
int height,
const GdkDmabuf *dmabuf,
int color_state_hint,
int range_hint,
int target);
void gdk_dmabuf_get_egl_yuv_hints (const GdkDmabuf *dmabuf,
GdkColorState *color_state,
int *color_space_hint,
int *range_hint);
#endif /* HAVE_DMABUF && HAVE_EGL */

View File

@@ -208,12 +208,9 @@ gdk_dmabuf_texture_new_from_builder (GdkDmabufTextureBuilder *builder,
gboolean is_yuv;
if (gdk_dmabuf_fourcc_is_yuv (dmabuf.fourcc, &is_yuv) && is_yuv)
{
g_warning_once ("FIXME: Implement the proper colorstate for YUV dmabufs");
color_state = gdk_color_state_get_srgb ();
}
color_state = GDK_COLOR_STATE_YUV;
else
color_state = gdk_color_state_get_srgb ();
color_state = GDK_COLOR_STATE_SRGB;
}
self = g_object_new (GDK_TYPE_DMABUF_TEXTURE,

View File

@@ -2198,6 +2198,8 @@ gdk_gl_context_import_dmabuf_for_target (GdkGLContext *self,
int width,
int height,
const GdkDmabuf *dmabuf,
int color_space_hint,
int range_hint,
int target)
{
#if defined(HAVE_EGL) && defined(HAVE_DMABUF)
@@ -2209,6 +2211,8 @@ gdk_gl_context_import_dmabuf_for_target (GdkGLContext *self,
width,
height,
dmabuf,
color_space_hint,
range_hint,
target);
if (image == EGL_NO_IMAGE)
return 0;
@@ -2232,6 +2236,8 @@ gdk_gl_context_import_dmabuf (GdkGLContext *self,
int width,
int height,
const GdkDmabuf *dmabuf,
int color_space_hint,
int range_hint,
gboolean *external)
{
GdkDisplay *display = gdk_gl_context_get_display (self);
@@ -2248,6 +2254,8 @@ gdk_gl_context_import_dmabuf (GdkGLContext *self,
texture_id = gdk_gl_context_import_dmabuf_for_target (self,
width, height,
dmabuf,
color_space_hint,
range_hint,
GL_TEXTURE_2D);
if (texture_id == 0)
{
@@ -2277,6 +2285,8 @@ gdk_gl_context_import_dmabuf (GdkGLContext *self,
texture_id = gdk_gl_context_import_dmabuf_for_target (self,
width, height,
dmabuf,
color_space_hint,
range_hint,
GL_TEXTURE_EXTERNAL_OES);
if (texture_id == 0)
{
@@ -2309,6 +2319,8 @@ gdk_gl_context_import_dmabuf (GdkGLContext *self,
texture_id = gdk_gl_context_import_dmabuf_for_target (self,
width, height,
dmabuf,
color_space_hint,
range_hint,
target);
if (texture_id == 0)

View File

@@ -185,6 +185,8 @@ guint gdk_gl_context_import_dmabuf (GdkGLContext
int width,
int height,
const GdkDmabuf *dmabuf,
int color_space_hint,
int range_hint,
gboolean *external);
gboolean gdk_gl_context_export_dmabuf (GdkGLContext *self,

View File

@@ -983,6 +983,8 @@ gdk_texture_download_surface (GdkTexture *texture,
* %CAIRO_FORMAT_ARGB32, so every downloaded pixel requires
* 4 bytes of memory.
*
* The downloaded data is converted to the sRGB color state.
*
* Downloading a texture into a Cairo image surface:
* ```c
* surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,

865
gdk/loaders/gdkavif.c Normal file
View File

@@ -0,0 +1,865 @@
#include "config.h"
#include "gdkavifprivate.h"
#include "gdkmemorytexturebuilder.h"
#include "gdktexture.h"
#include "gdkdisplay.h"
#include "gdkcicpparamsprivate.h"
#include "gdkcolorstateprivate.h"
#include "gdkmemoryformatprivate.h"
#include "gdkdmabuftexture.h"
#include "gdktexturedownloader.h"
#include <avif/avif.h>
#ifdef HAVE_DMABUF
#include "gdkdmabuftextureprivate.h"
#include "gdkdmabuftexturebuilder.h"
#include "gdkdmabuffourccprivate.h"
/* NOTE: We build this code outside of libgtk, in tests and the image tool, so this
* code has to be a bit careful to avoid depending on private api, which can also
* be dragged in indirectly, via inlines.
*/
#ifdef AVIF_DEBUG
#define DEBUG(fmt,...) g_log ("avif", G_LOG_LEVEL_DEBUG, fmt __VA_OPT__(,) __VA_ARGS__)
#else
#define DEBUG(fmt,...)
#endif
/* {{{ udmabuf support */
#include <inttypes.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/udmabuf.h>
#include <drm_fourcc.h>
#include <errno.h>
#include <gio/gio.h>
static int udmabuf_fd;
static gboolean
udmabuf_initialize (GError **error)
{
if (udmabuf_fd == 0)
{
udmabuf_fd = open ("/dev/udmabuf", O_RDWR);
if (udmabuf_fd == -1)
{
g_set_error (error,
G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to open /dev/udmabuf: %s",
g_strerror (errno));
}
}
return udmabuf_fd != -1;
}
typedef struct
{
int mem_fd;
int dmabuf_fd;
size_t size;
gpointer data;
} UDmabuf;
static void
udmabuf_free (gpointer data)
{
UDmabuf *udmabuf = data;
munmap (udmabuf->data, udmabuf->size);
close (udmabuf->mem_fd);
close (udmabuf->dmabuf_fd);
g_free (udmabuf);
}
#define align(x,y) (((x) + (y) - 1) & ~((y) - 1))
static UDmabuf *
udmabuf_allocate (size_t size,
GError **error)
{
int mem_fd = -1;
int dmabuf_fd = -1;
uint64_t alignment;
int res;
gpointer data;
UDmabuf *udmabuf;
if (udmabuf_fd == -1)
{
g_set_error (error,
G_IO_ERROR, G_IO_ERROR_FAILED,
"udmabuf not available");
goto fail;
}
alignment = sysconf (_SC_PAGE_SIZE);
size = align (size, alignment);
mem_fd = memfd_create ("gtk", MFD_ALLOW_SEALING);
if (mem_fd == -1)
{
g_set_error (error,
G_IO_ERROR, G_IO_ERROR_FAILED,
"Failed to open /dev/udmabuf: %s",
g_strerror (errno));
goto fail;
}
res = ftruncate (mem_fd, size);
if (res == -1)
{
g_set_error (error,
G_IO_ERROR, G_IO_ERROR_FAILED,
"ftruncate failed: %s",
g_strerror (errno));
goto fail;
}
if (fcntl (mem_fd, F_ADD_SEALS, F_SEAL_SHRINK) < 0)
{
g_set_error (error,
G_IO_ERROR, G_IO_ERROR_FAILED,
"ftruncate failed: %s",
g_strerror (errno));
goto fail;
}
dmabuf_fd = ioctl (udmabuf_fd,
UDMABUF_CREATE,
&(struct udmabuf_create) {
.memfd = mem_fd,
.flags = UDMABUF_FLAGS_CLOEXEC,
.offset = 0,
.size = size
});
if (dmabuf_fd < 0)
{
g_set_error (error,
G_IO_ERROR, G_IO_ERROR_FAILED,
"UDMABUF_CREATE ioctl failed: %s",
g_strerror (errno));
goto fail;
}
data = mmap (NULL, size, PROT_WRITE | PROT_READ, MAP_SHARED, mem_fd, 0);
if (!data)
{
g_set_error (error,
G_IO_ERROR, G_IO_ERROR_FAILED,
"mmap failed: %s",
g_strerror (errno));
goto fail;
}
udmabuf = g_new0 (UDmabuf, 1);
udmabuf->mem_fd = mem_fd;
udmabuf->dmabuf_fd = dmabuf_fd;
udmabuf->size = size;
udmabuf->data = data;
return udmabuf;
fail:
if (mem_fd != -1)
close (mem_fd);
if (dmabuf_fd != -1)
close (dmabuf_fd);
return NULL;
}
/* }}} */
/* {{{ dmabuf texture support */
static GdkTexture *
gdk_avif_create_dmabuf_texture (avifDecoder *decoder,
GdkColorState *color_state,
GError **error)
{
guint fourcc = 0;
guint32 width, height, depth;
GdkDmabufTextureBuilder *builder;
UDmabuf *udmabuf;
gboolean combine_uv = FALSE;
guchar *data;
gsize size0, size1, size2;
GdkTexture *texture;
if (!udmabuf_initialize (error))
return NULL;
width = decoder->image->width;
height = decoder->image->height;
depth = decoder->image->depth;
if (decoder->image->alphaPlane)
{
g_set_error (error,
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT,
"no yuv dmabuf with alpha");
return NULL;
}
switch (decoder->image->yuvFormat)
{
case AVIF_PIXEL_FORMAT_YUV444:
DEBUG ("load: format yuv444");
if (depth == 8)
fourcc = DRM_FORMAT_YUV444;
break;
case AVIF_PIXEL_FORMAT_YUV422:
DEBUG ("load: format yuv422");
if (depth == 8)
fourcc = DRM_FORMAT_YUV422;
break;
case AVIF_PIXEL_FORMAT_YUV420:
DEBUG ("load: format yuv420");
combine_uv = TRUE;
if (depth == 8)
fourcc = DRM_FORMAT_NV12;
else if (depth == 10)
fourcc = DRM_FORMAT_P010;
else if (depth == 12)
fourcc = DRM_FORMAT_P012;
else if (depth == 16)
fourcc = DRM_FORMAT_P016;
break;
case AVIF_PIXEL_FORMAT_YUV400:
case AVIF_PIXEL_FORMAT_NONE:
case AVIF_PIXEL_FORMAT_COUNT:
default:
break;
}
if (fourcc == 0)
{
const char *names[] = { "none", "yuv444", "yuv422", "yuv420", "yuv400" };
g_set_error (error,
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT,
"unsupported pixel format %s, depth %u",
names[decoder->image->yuvFormat], decoder->image->depth);
return NULL;
}
DEBUG ("load: use fourcc %.4s", (char *)&fourcc);
builder = gdk_dmabuf_texture_builder_new ();
gdk_dmabuf_texture_builder_set_display (builder, gdk_display_get_default ());
gdk_dmabuf_texture_builder_set_width (builder, width);
gdk_dmabuf_texture_builder_set_height (builder, height);
gdk_dmabuf_texture_builder_set_color_state (builder, color_state);
gdk_dmabuf_texture_builder_set_fourcc (builder, fourcc);
gdk_dmabuf_texture_builder_set_modifier (builder, DRM_FORMAT_MOD_LINEAR);
gdk_dmabuf_texture_builder_set_premultiplied (builder, FALSE);
if (combine_uv)
{
size0 = height * avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_Y);
size1 = height / 2 * avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_Y);
udmabuf = udmabuf_allocate (size0 + size1, NULL);
data = (guchar *) udmabuf->data;
if (depth == 8)
{
memcpy (data, avifImagePlane (decoder->image, AVIF_CHAN_Y), size0);
for (int i = 0; i < height / 2; i++)
{
guchar *usrc = avifImagePlane (decoder->image, AVIF_CHAN_U) + i * avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_U);
guchar *vsrc = avifImagePlane (decoder->image, AVIF_CHAN_V) + i * avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_V);
guchar *dest = data + size0 + i * avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_Y);
for (int j = 0; j < width / 2; j++)
{
dest[2*j] = usrc[j];
dest[2*j+1] = vsrc[j];
}
}
}
else
{
for (int i = 0; i < height; i++)
{
guint16 *src = (guint16 *) (avifImagePlane (decoder->image, AVIF_CHAN_Y) + i * avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_Y));
guint16 *dest = (guint16 *) (data + i * avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_Y));
for (int j = 0; j < width; j++)
dest[j] = src[j] << (16 - depth);
}
for (int i = 0; i < height / 2; i++)
{
guint16 *usrc = (guint16 *)(avifImagePlane (decoder->image, AVIF_CHAN_U) + i * avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_U));
guint16 *vsrc = (guint16 *)(avifImagePlane (decoder->image, AVIF_CHAN_V) + i * avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_V));
guint16 *dest = (guint16 *)(data + size0 + i * avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_Y));
for (int j = 0; j < width / 2; j++)
{
dest[2*j] = usrc[j] << (16 - depth);
dest[2*j+1] = vsrc[j] << (16 - depth);
}
}
}
gdk_dmabuf_texture_builder_set_n_planes (builder, 2);
gdk_dmabuf_texture_builder_set_fd (builder, 0, udmabuf->dmabuf_fd);
gdk_dmabuf_texture_builder_set_offset (builder, 0, 0);
gdk_dmabuf_texture_builder_set_stride (builder, 0, avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_Y));
gdk_dmabuf_texture_builder_set_fd (builder, 1, udmabuf->dmabuf_fd);
gdk_dmabuf_texture_builder_set_offset (builder, 1, size0);
gdk_dmabuf_texture_builder_set_stride (builder, 1, avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_Y));
}
else
{
size0 = height * avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_Y);
size1 = height * avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_U);
size2 = height * avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_V);
udmabuf = udmabuf_allocate (size0 + size1 + size2, NULL);
data = (guchar *) udmabuf->data;
memcpy (data, avifImagePlane (decoder->image, AVIF_CHAN_Y), size0);
memcpy (data + size0, avifImagePlane (decoder->image, AVIF_CHAN_U), size1);
memcpy (data + size0 + size1, avifImagePlane (decoder->image, AVIF_CHAN_V), size2);
gdk_dmabuf_texture_builder_set_n_planes (builder, 3);
gdk_dmabuf_texture_builder_set_fd (builder, 0, udmabuf->dmabuf_fd);
gdk_dmabuf_texture_builder_set_offset (builder, 0, 0);
gdk_dmabuf_texture_builder_set_stride (builder, 0, avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_Y));
gdk_dmabuf_texture_builder_set_fd (builder, 1, udmabuf->dmabuf_fd);
gdk_dmabuf_texture_builder_set_offset (builder, 1, size0);
gdk_dmabuf_texture_builder_set_stride (builder, 1, avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_U));
gdk_dmabuf_texture_builder_set_fd (builder, 2, udmabuf->dmabuf_fd);
gdk_dmabuf_texture_builder_set_offset (builder, 2, size0 + size1);
gdk_dmabuf_texture_builder_set_stride (builder, 2, avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_V));
}
texture = gdk_dmabuf_texture_builder_build (builder, udmabuf_free, udmabuf, error);
g_object_unref (builder);
return texture;
}
#endif /* HAVE_DMABUF */
/* }}} */
/* {{{ memory texture support */
static GdkTexture *
gdk_avif_create_memory_texture (avifDecoder *decoder,
GdkColorState *color_state,
GError **error)
{
guint32 width, height, depth, stride, bpp;
GdkTexture *texture;
GdkMemoryTextureBuilder *builder;
guchar *data;
GBytes *bytes;
GdkMemoryFormat format = GDK_MEMORY_N_FORMATS;
int X_SUB, Y_SUB;
width = decoder->image->width;
height = decoder->image->height;
depth = decoder->image->depth;
switch (decoder->image->yuvFormat)
{
case AVIF_PIXEL_FORMAT_YUV444:
X_SUB = 1; Y_SUB = 1;
if (depth == 8)
{
format = GDK_MEMORY_R8G8B8A8;
bpp = 4;
}
else
{
format = GDK_MEMORY_R16G16B16A16;
bpp = 8;
}
break;
case AVIF_PIXEL_FORMAT_YUV422:
X_SUB = 2; Y_SUB = 1;
if (depth == 8)
{
format = GDK_MEMORY_R8G8B8A8;
bpp = 8;
}
else
{
format = GDK_MEMORY_R16G16B16A16;
bpp = 8;
}
break;
case AVIF_PIXEL_FORMAT_YUV420:
X_SUB = 2; Y_SUB = 2;
break;
case AVIF_PIXEL_FORMAT_YUV400:
case AVIF_PIXEL_FORMAT_NONE:
case AVIF_PIXEL_FORMAT_COUNT:
if (depth == 8)
{
format = GDK_MEMORY_R8G8B8A8;
bpp = 4;
}
else
{
format = GDK_MEMORY_R16G16B16A16;
bpp = 8;
}
default:
break;
}
if (format == GDK_MEMORY_N_FORMATS)
{
const char *names[] = { "none", "yuv444", "yuv422", "yuv420", "yuv400" };
g_set_error (error,
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT,
"unsupported pixel format for YUVA %s, depth %u",
names[decoder->image->yuvFormat], depth);
return NULL;
}
stride = width * bpp;
data = g_malloc (stride * height);
if (depth == 8)
{
guchar *y_data = avifImagePlane (decoder->image, AVIF_CHAN_Y);
guchar *u_data = avifImagePlane (decoder->image, AVIF_CHAN_U);
guchar *v_data = avifImagePlane (decoder->image, AVIF_CHAN_V);
guchar *a_data = avifImagePlane (decoder->image, AVIF_CHAN_A);
guchar *dst_data = data;
gsize y_stride = avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_Y);
gsize u_stride = avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_U);
gsize v_stride = avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_V);
gsize a_stride = avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_A);
gsize dst_stride = stride;
for (int y = 0; y < height; y += Y_SUB)
{
for (int x = 0; x < width; x += X_SUB)
{
guint y_, u_, v_, a_;
u_ = u_data[x / X_SUB];
v_ = v_data[x / X_SUB];
for (int ys = 0; ys < Y_SUB && y + ys < height; ys++)
for (int xs = 0; xs < X_SUB && x + xs < width; xs++)
{
guchar *dest = &dst_data[4 * (x + xs) + dst_stride * ys];
y_ = y_data[x + xs + y_stride * ys];
a_ = a_data ? a_data[x + xs + a_stride * ys] : 0xff;
dest[0] = y_;
dest[1] = u_;
dest[2] = v_;
dest[3] = a_;
}
}
dst_data += Y_SUB * dst_stride;
y_data += Y_SUB * y_stride;
u_data += u_stride;
v_data += v_stride;
if (a_data)
a_data += a_stride;
}
}
else
{
guint16 *y_data = (guint16 *) avifImagePlane (decoder->image, AVIF_CHAN_Y);
guint16 *u_data = (guint16 *) avifImagePlane (decoder->image, AVIF_CHAN_U);
guint16 *v_data = (guint16 *) avifImagePlane (decoder->image, AVIF_CHAN_V);
guint16 *a_data = (guint16 *) avifImagePlane (decoder->image, AVIF_CHAN_A);
guint16 *dst_data = (guint16 *) data;
gsize y_stride = avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_Y) / 2;
gsize u_stride = avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_U) / 2;
gsize v_stride = avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_V) / 2;
gsize a_stride = avifImagePlaneRowBytes (decoder->image, AVIF_CHAN_A) / 2;
gsize dst_stride = stride / 2;
for (int y = 0; y < height; y += Y_SUB)
{
for (int x = 0; x < width; x += X_SUB)
{
guint y_, u_, v_, a_;
u_ = u_data[x / X_SUB] << (16 - depth);
v_ = v_data[x / X_SUB] << (16 - depth);
for (int ys = 0; ys < Y_SUB && y + ys < height; ys++)
for (int xs = 0; xs < X_SUB && x + xs < width; xs++)
{
guint16 *dest = &dst_data[4 * (x + xs) + dst_stride * ys];
y_ = y_data[x + xs + y_stride * ys] << (16 - depth);
a_ = (a_data ? a_data[x + xs + a_stride * ys] : 0xffff) << (16 - depth);
dest[0] = y_ | (y_ >> depth);
dest[1] = u_ | (u_ >> depth);
dest[2] = v_ | (v_ >> depth);
dest[3] = a_ | (a_ >> depth);
}
}
dst_data += Y_SUB * dst_stride;
y_data += Y_SUB * y_stride;
u_data += u_stride;
v_data += v_stride;
}
}
builder = gdk_memory_texture_builder_new ();
bytes = g_bytes_new_take (data, stride * height);
gdk_memory_texture_builder_set_width (builder, width);
gdk_memory_texture_builder_set_height (builder, height);
gdk_memory_texture_builder_set_bytes (builder, bytes);
gdk_memory_texture_builder_set_stride (builder, stride);
gdk_memory_texture_builder_set_format (builder, format);
gdk_memory_texture_builder_set_color_state (builder, color_state);
texture = gdk_memory_texture_builder_build (builder);
g_bytes_unref (bytes);
g_object_unref (builder);
return texture;
}
/* }}} */
/* {{{ Public API */
GdkTexture *
gdk_load_avif (GBytes *bytes,
GError **error)
{
avifDecoder *decoder;
avifResult result;
GdkCicpParams *params;
GdkColorState *color_state = NULL;
GdkTexture *texture = NULL;
GError *local_error = NULL;
decoder = avifDecoderCreate ();
result = avifDecoderSetIOMemory (decoder,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes));
if (result != AVIF_RESULT_OK)
{
g_set_error (error,
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT,
"avifDecoderSetIOFile failed: %u", result);
goto fail;
}
result = avifDecoderParse (decoder);
if (result != AVIF_RESULT_OK)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"avifDecoderParse failed: %u", result);
goto fail;
}
result = avifDecoderNextImage (decoder);
if (result != AVIF_RESULT_OK)
{
g_set_error (error,
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT,
"avifDecoderNextImage failed: %u", result);
goto fail;
}
DEBUG ("load: depth %u", decoder->image->depth);
DEBUG ("load: cicp %u/%u/%u/%u",
decoder->image->colorPrimaries,
decoder->image->transferCharacteristics,
decoder->image->matrixCoefficients,
decoder->image->yuvRange);
params = gdk_cicp_params_new ();
gdk_cicp_params_set_color_primaries (params, decoder->image->colorPrimaries);
gdk_cicp_params_set_transfer_function (params, decoder->image->transferCharacteristics);
gdk_cicp_params_set_matrix_coefficients (params, decoder->image->matrixCoefficients);
gdk_cicp_params_set_range (params, decoder->image->yuvRange == AVIF_RANGE_LIMITED
? GDK_CICP_RANGE_NARROW
: GDK_CICP_RANGE_FULL);
color_state = gdk_cicp_params_build_color_state (params, error);
g_object_unref (params);
if (!color_state)
goto fail;
#ifdef HAVE_DMABUF
texture = gdk_avif_create_dmabuf_texture (decoder, color_state, &local_error);
if (!texture)
{
DEBUG ("load: creating dmabuf failed: %s", local_error->message);
g_clear_error (&local_error);
}
#endif
if (!texture)
texture = gdk_avif_create_memory_texture (decoder, color_state, error);
(gdk_color_state_unref) (color_state);
fail:
avifDecoderDestroy (decoder);
return texture;
}
static int
bytes_per_channel (GdkMemoryFormat format)
{
switch (format)
{
case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED:
case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED:
case GDK_MEMORY_B8G8R8A8:
case GDK_MEMORY_A8R8G8B8:
case GDK_MEMORY_R8G8B8A8:
case GDK_MEMORY_A8B8G8R8:
case GDK_MEMORY_R8G8B8:
case GDK_MEMORY_B8G8R8:
case GDK_MEMORY_G8A8_PREMULTIPLIED:
case GDK_MEMORY_G8A8:
case GDK_MEMORY_G8:
case GDK_MEMORY_A8:
case GDK_MEMORY_A8B8G8R8_PREMULTIPLIED:
case GDK_MEMORY_B8G8R8X8:
case GDK_MEMORY_X8R8G8B8:
case GDK_MEMORY_R8G8B8X8:
case GDK_MEMORY_X8B8G8R8:
return 1;
case GDK_MEMORY_R16G16B16:
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
case GDK_MEMORY_R16G16B16A16:
case GDK_MEMORY_R16G16B16_FLOAT:
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
case GDK_MEMORY_R16G16B16A16_FLOAT:
case GDK_MEMORY_G16A16_PREMULTIPLIED:
case GDK_MEMORY_G16A16:
case GDK_MEMORY_G16:
case GDK_MEMORY_A16:
case GDK_MEMORY_A16_FLOAT:
return 2;
case GDK_MEMORY_R32G32B32_FLOAT:
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
case GDK_MEMORY_R32G32B32A32_FLOAT:
case GDK_MEMORY_A32_FLOAT:
return 4;
case GDK_MEMORY_N_FORMATS:
default:
g_assert_not_reached ();
}
}
GBytes *
gdk_save_avif (GdkTexture *texture)
{
uint32_t width, height, depth;
GdkColorState *color_state;
const GdkCicp *cicp;
avifEncoder *encoder;
avifImage *image = NULL;
avifRWData output = AVIF_DATA_EMPTY;
GdkTextureDownloader *downloader;
GBytes *bytes = NULL;
gsize stride;
GBytes *out_bytes = NULL;
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
if (bytes_per_channel (gdk_texture_get_format (texture)) == 1)
depth = 8;
else
depth = 12;
color_state = gdk_texture_get_color_state (texture);
cicp = gdk_color_state_get_cicp (color_state);
DEBUG ("save: depth %u", depth);
DEBUG ("save: cicp %u/%u/%u/%u",
cicp->color_primaries,
cicp->transfer_function,
cicp->matrix_coefficients,
cicp->range);
downloader = gdk_texture_downloader_new (texture);
if (depth == 8)
gdk_texture_downloader_set_format (downloader, GDK_MEMORY_R8G8B8A8);
else
gdk_texture_downloader_set_format (downloader, GDK_MEMORY_R16G16B16A16);
gdk_texture_downloader_set_color_state (downloader, gdk_texture_get_color_state (texture));
bytes = gdk_texture_downloader_download_bytes (downloader, &stride);
gdk_texture_downloader_free (downloader);
if (cicp->matrix_coefficients != 0)
{
/* some form of yuv */
image = avifImageCreate (width, height, depth, AVIF_PIXEL_FORMAT_YUV444);
image->colorPrimaries = cicp->color_primaries;
image->transferCharacteristics = cicp->transfer_function;
image->matrixCoefficients = cicp->matrix_coefficients;
image->yuvRange = cicp->range == GDK_CICP_RANGE_NARROW
? AVIF_RANGE_LIMITED
: AVIF_RANGE_FULL;
avifImageAllocatePlanes (image, AVIF_PLANES_YUV);
if (depth == 8)
{
for (gsize y = 0; y < height; y++)
{
const guchar *orig = (guchar *) g_bytes_get_data (bytes, NULL) + y * stride;
guchar *y_ = avifImagePlane (image, AVIF_CHAN_Y) + y * avifImagePlaneRowBytes (image, AVIF_CHAN_Y);
guchar *u_ = avifImagePlane (image, AVIF_CHAN_U) + y * avifImagePlaneRowBytes (image, AVIF_CHAN_U);
guchar *v_ = avifImagePlane (image, AVIF_CHAN_V) + y * avifImagePlaneRowBytes (image, AVIF_CHAN_V);
for (gsize x = 0; x < width; x++)
{
y_[x] = orig[4*x + 0];
u_[x] = orig[4*x + 1];
v_[x] = orig[4*x + 2];
}
}
}
else
{
for (gsize y = 0; y < height; y++)
{
const guint16 *orig = (guint16 *) ((guchar *) g_bytes_get_data (bytes, NULL) + y * stride);
guint16 *y_ = (guint16 *) (avifImagePlane (image, AVIF_CHAN_Y) + y * avifImagePlaneRowBytes (image, AVIF_CHAN_Y));
guint16 *u_ = (guint16 *) (avifImagePlane (image, AVIF_CHAN_U) + y * avifImagePlaneRowBytes (image, AVIF_CHAN_U));
guint16 *v_ = (guint16 *) (avifImagePlane (image, AVIF_CHAN_V) + y * avifImagePlaneRowBytes (image, AVIF_CHAN_V));
for (gsize x = 0; x < width; x++)
{
y_[x] = orig[4*x + 0] >> (16 - depth);
u_[x] = orig[4*x + 1] >> (16 - depth);
v_[x] = orig[4*x + 2] >> (16 - depth);
}
}
}
}
else
{
avifRGBImage rgb;
image = avifImageCreate (width, height, depth, AVIF_PIXEL_FORMAT_YUV444);
image->colorPrimaries = cicp->color_primaries;
image->transferCharacteristics = cicp->transfer_function;
image->matrixCoefficients = cicp->matrix_coefficients;
image->yuvRange = cicp->range == GDK_CICP_RANGE_NARROW
? AVIF_RANGE_LIMITED
: AVIF_RANGE_FULL;
avifRGBImageSetDefaults (&rgb, image);
avifRGBImageAllocatePixels (&rgb);
if (depth == 8)
{
for (gsize y = 0; y < height; y++)
{
const guchar *orig = (guchar *) g_bytes_get_data (bytes, NULL) + y * stride;
guchar *pixels = rgb.pixels + y * rgb.rowBytes;
for (gsize x = 0; x < width; x++)
{
pixels[4*x + 0] = orig[4*x + 0];
pixels[4*x + 1] = orig[4*x + 1];
pixels[4*x + 2] = orig[4*x + 2];
pixels[4*x + 3] = orig[4*x + 3];
}
}
}
else
{
for (gsize y = 0; y < height; y++)
{
const guint16 *orig = (const guint16 *) ((guchar *) g_bytes_get_data (bytes, NULL) + y * stride);
guint16 *pixels = (guint16 *) (rgb.pixels + y * rgb.rowBytes);
for (gsize x = 0; x < width; x++)
{
pixels[4*x + 0] = orig[4*x + 0] >> (16 - depth);
pixels[4*x + 1] = orig[4*x + 1] >> (16 - depth);
pixels[4*x + 2] = orig[4*x + 2] >> (16 - depth);
pixels[4*x + 3] = orig[4*x + 3] >> (16 - depth);
}
}
}
avifImageRGBToYUV(image, &rgb);
avifRGBImageFreePixels (&rgb);
}
DEBUG ("save: cicp in image %u/%u/%u/%u",
image->colorPrimaries,
image->transferCharacteristics,
image->matrixCoefficients,
image->yuvRange);
encoder = avifEncoderCreate ();
if (avifEncoderWrite (encoder, image, &output) != AVIF_RESULT_OK)
goto fail;
out_bytes = g_bytes_new_take (output.data, output.size);
fail:
g_clear_pointer (&bytes, g_bytes_unref);
avifEncoderDestroy (encoder);
if (image)
avifImageDestroy (image);
return out_bytes;
}
gboolean
gdk_is_avif (GBytes *bytes)
{
avifROData input;
input.data = g_bytes_get_data (bytes, &input.size);
return avifPeekCompatibleFileType (&input);
}
/* }}} */
/* vim:set foldmethod=marker expandtab: */

View File

@@ -0,0 +1,28 @@
/* GDK - The GIMP Drawing Kit
* Copyright (C) 2024 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gdk.h>
#include <gio/gio.h>
GdkTexture *gdk_load_avif (GBytes *bytes,
GError **error);
GBytes *gdk_save_avif (GdkTexture *texture);
gboolean gdk_is_avif (GBytes *bytes);

View File

@@ -149,37 +149,39 @@ gdk_load_jpeg (GBytes *input_bytes,
g_bytes_get_size (input_bytes));
jpeg_read_header (&info, TRUE);
if (info.jpeg_color_space == JCS_GRAYSCALE)
{
color_state = GDK_COLOR_STATE_SRGB;
info.out_color_space = JCS_GRAYSCALE;
format = GDK_MEMORY_G8;
}
else if (info.jpeg_color_space == JCS_YCbCr)
{
color_state = GDK_COLOR_STATE_JPEG;
info.out_color_space = JCS_YCbCr;
format = GDK_MEMORY_R8G8B8;
}
else if (info.jpeg_color_space == JCS_CMYK)
{
color_state = GDK_COLOR_STATE_SRGB;
info.out_color_space = JCS_CMYK;
format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
}
else
{
color_state = GDK_COLOR_STATE_SRGB;
info.out_color_space = JCS_RGB;
format = GDK_MEMORY_R8G8B8;
}
jpeg_start_decompress (&info);
width = info.output_width;
height = info.output_height;
stride = gdk_memory_format_bytes_per_pixel (format) * width;
color_state = GDK_COLOR_STATE_SRGB;
switch ((int)info.out_color_space)
{
case JCS_GRAYSCALE:
stride = width;
data = g_try_malloc_n (stride, height);
format = GDK_MEMORY_G8;
break;
case JCS_RGB:
stride = 3 * width;
data = g_try_malloc_n (stride, height);
format = GDK_MEMORY_R8G8B8;
break;
case JCS_CMYK:
stride = 4 * width;
data = g_try_malloc_n (stride, height);
format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
break;
default:
g_set_error (error,
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT,
_("Unsupported JPEG colorspace (%d)"), info.out_color_space);
jpeg_destroy_decompress (&info);
return NULL;
}
data = g_try_malloc_n (stride, height);
if (!data)
{
@@ -215,7 +217,6 @@ gdk_load_jpeg (GBytes *input_bytes,
texture = gdk_memory_texture_builder_build (builder);
gdk_color_state_unref (color_state);
g_object_unref (builder);
g_bytes_unref (bytes);
@@ -239,6 +240,7 @@ gdk_save_jpeg (GdkTexture *texture)
gsize texstride;
guchar *row;
int width, height;
GdkColorState *color_state;
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
@@ -268,13 +270,24 @@ gdk_save_jpeg (GdkTexture *texture)
jpeg_set_defaults (&info);
jpeg_set_quality (&info, 75, TRUE);
color_state = gdk_texture_get_color_state (texture);
if (gdk_color_state_equal (color_state, GDK_COLOR_STATE_JPEG))
{
info.in_color_space = JCS_YCbCr;
}
else
{
info.in_color_space = JCS_RGB;
color_state = GDK_COLOR_STATE_SRGB;
}
info.mem->max_memory_to_use = 300 * 1024 * 1024;
jpeg_mem_dest (&info, &data, &size);
gdk_texture_downloader_init (&downloader, texture);
gdk_texture_downloader_set_format (&downloader, GDK_MEMORY_R8G8B8);
gdk_texture_downloader_set_color_state (&downloader, GDK_COLOR_STATE_SRGB);
gdk_texture_downloader_set_color_state (&downloader, color_state);
texbytes = gdk_texture_downloader_download_bytes (&downloader, &texstride);
gdk_texture_downloader_finish (&downloader);
texdata = g_bytes_get_data (texbytes, NULL);

View File

@@ -74,6 +74,12 @@ gdk_public_sources = files([
'loaders/gdkjpeg.c',
])
if avif_dep.found()
gdk_public_sources += [
'loaders/gdkavif.c'
]
endif
gdk_public_headers = files([
'gdk.h',
'gdkapplaunchcontext.h',
@@ -237,6 +243,7 @@ gdk_deps = [
png_dep,
tiff_dep,
jpeg_dep,
avif_dep,
]
if profiler_enabled

View File

@@ -43,6 +43,7 @@
#include <gdk/gdkmemoryformatprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdktextureprivate.h>
#include <gdk/gdkdmabufeglprivate.h>
#include <gdk/gdkmemoryformatprivate.h>
#include <gdk/gdkdmabuftextureprivate.h>
@@ -814,6 +815,8 @@ gsk_gl_driver_import_dmabuf_texture (GskGLDriver *self,
gboolean external;
GdkMemoryFormat format;
gboolean premultiply;
GdkColorState *color_state;
int color_space, range;
gdk_gl_context_make_current (context);
@@ -831,10 +834,16 @@ gsk_gl_driver_import_dmabuf_texture (GskGLDriver *self,
dmabuf = gdk_dmabuf_texture_get_dmabuf (texture);
format = gdk_texture_get_format (GDK_TEXTURE (texture));
premultiply = gdk_memory_format_alpha (format) == GDK_MEMORY_ALPHA_STRAIGHT;
color_state = gdk_texture_get_color_state (GDK_TEXTURE (texture));
gdk_dmabuf_get_egl_yuv_hints (dmabuf, color_state, &color_space, &range);
texture_id = gdk_gl_context_import_dmabuf (context,
width, height,
dmabuf,
color_space,
range,
&external);
if (texture_id == 0)
return 0;

View File

@@ -10,6 +10,7 @@
#include "gskglimageprivate.h"
#include "gdkdmabuftextureprivate.h"
#include "gdkdmabufeglprivate.h"
#include "gdkglcontextprivate.h"
#include "gdkgltextureprivate.h"
@@ -92,7 +93,7 @@ gsk_gl_frame_upload_texture (GskGpuFrame *frame,
gdk_gl_texture_get_id (gl_texture),
FALSE,
gdk_gl_texture_has_mipmap (gl_texture) ? (GSK_GPU_IMAGE_CAN_MIPMAP | GSK_GPU_IMAGE_MIPMAP) : 0);
/* This is a hack, but it works */
sync = gdk_gl_texture_get_sync (gl_texture);
if (sync)
@@ -105,19 +106,56 @@ gsk_gl_frame_upload_texture (GskGpuFrame *frame,
{
gboolean external;
GLuint tex_id;
int color_space_hint = 0;
int range_hint = 0;
#if defined (HAVE_DMABUF) && defined (HAVE_EGL)
gdk_dmabuf_get_egl_yuv_hints (gdk_dmabuf_texture_get_dmabuf (GDK_DMABUF_TEXTURE (texture)),
gdk_texture_get_color_state (texture),
&color_space_hint,
&range_hint);
#endif
tex_id = gdk_gl_context_import_dmabuf (GDK_GL_CONTEXT (gsk_gpu_frame_get_context (frame)),
gdk_texture_get_width (texture),
gdk_texture_get_height (texture),
gdk_dmabuf_texture_get_dmabuf (GDK_DMABUF_TEXTURE (texture)),
color_space_hint,
range_hint,
&external);
if (tex_id)
{
GskGpuImageFlags flags = 0;
if (external)
flags |= GSK_GPU_IMAGE_EXTERNAL | GSK_GPU_IMAGE_NO_BLIT;
#if defined (HAVE_DMABUF) && defined (HAVE_EGL)
switch (color_space_hint)
{
case EGL_ITU_REC709_EXT:
flags |= GSK_GPU_IMAGE_BT709;
break;
case EGL_ITU_REC601_EXT:
flags |= GSK_GPU_IMAGE_BT601;
break;
case EGL_ITU_REC2020_EXT:
flags |= GSK_GPU_IMAGE_BT2020;
break;
default:
break;
}
if (range_hint == EGL_YUV_NARROW_RANGE_EXT)
flags |= GSK_GPU_IMAGE_NARROW_RANGE;
#endif
return gsk_gl_image_new_for_texture (GSK_GL_DEVICE (gsk_gpu_frame_get_device (frame)),
texture,
tex_id,
TRUE,
(external ? GSK_GPU_IMAGE_EXTERNAL | GSK_GPU_IMAGE_NO_BLIT : 0));
flags);
}
}

View File

@@ -682,7 +682,7 @@ gsk_gpu_cache_lookup_tile (GskGpuCache *self,
gsk_gpu_cached_use (self, (GskGpuCached *) tile);
*out_color_state = tile->color_state;
*out_color_state = gdk_color_state_ref (tile->color_state);
return g_object_ref (tile->image);
}

View File

@@ -36,7 +36,8 @@ gsk_gpu_convert_cicp_op_print_instance (GskGpuShaderOp *shader,
g_string_append_printf (string, "cicp %u/%u/%u/%u",
instance->color_primaries,
instance->transfer_function,
0, 1);
instance->matrix_coefficients,
instance->range);
}
static const GskGpuShaderOpClass GSK_GPU_CONVERT_OP_CLASS = {
@@ -88,6 +89,8 @@ gsk_gpu_convert_from_cicp_op (GskGpuFrame *frame,
instance->opacity = opacity;
instance->color_primaries = cicp->color_primaries;
instance->transfer_function = cicp->transfer_function;
instance->matrix_coefficients = cicp->matrix_coefficients;
instance->range = cicp->range == GDK_CICP_RANGE_NARROW ? 0 : 1;
}
void
@@ -118,4 +121,6 @@ gsk_gpu_convert_to_cicp_op (GskGpuFrame *frame,
instance->opacity = opacity;
instance->color_primaries = cicp->color_primaries;
instance->transfer_function = cicp->transfer_function;
instance->matrix_coefficients = cicp->matrix_coefficients;
instance->range = cicp->range == GDK_CICP_RANGE_NARROW ? 0 : 1;
}

View File

@@ -1,6 +1,7 @@
#include "config.h"
#include "gskgpuimageprivate.h"
#include "gdkcolorstateprivate.h"
typedef struct _GskGpuImagePrivate GskGpuImagePrivate;
@@ -162,3 +163,61 @@ gsk_gpu_image_get_projection_matrix (GskGpuImage *self,
{
GSK_GPU_IMAGE_GET_CLASS (self)->get_projection_matrix (self, out_projection);
}
GdkColorState *
gsk_gpu_image_adjust_color_state (GskGpuImage *self,
GdkColorState *color_state)
{
GskGpuImagePrivate *priv = gsk_gpu_image_get_instance_private (self);
GdkColorState *adjusted;
GdkCicp cicp;
adjusted = gdk_color_state_ref (color_state);
if (priv->flags & GSK_GPU_IMAGE_SRGB)
{
GdkColorState *no_srgb;
no_srgb = gdk_color_state_get_no_srgb_tf (adjusted);
g_assert (no_srgb);
gdk_color_state_unref (adjusted);
adjusted = gdk_color_state_ref (no_srgb);
}
if (!gdk_color_state_get_cicp (adjusted))
return adjusted;
cicp = *gdk_color_state_get_cicp (adjusted);
if (priv->flags & GSK_GPU_IMAGE_NARROW_RANGE)
cicp.range = GDK_CICP_RANGE_FULL;
switch (priv->flags & GSK_GPU_IMAGE_YUV)
{
case GSK_GPU_IMAGE_BT709:
g_assert (cicp.matrix_coefficients == 1);
cicp.matrix_coefficients = 0;
break;
case GSK_GPU_IMAGE_BT601:
g_assert (cicp.matrix_coefficients == 5 ||
cicp.matrix_coefficients == 6);
cicp.matrix_coefficients = 0;
break;
case GSK_GPU_IMAGE_BT2020:
g_assert (cicp.matrix_coefficients == 9);
cicp.matrix_coefficients = 0;
break;
default:
break;
}
if (!gdk_cicp_equal (&cicp, gdk_color_state_get_cicp (adjusted)))
{
gdk_color_state_unref (adjusted);
adjusted = gdk_color_state_new_for_cicp (&cicp, NULL);
g_assert (adjusted);
}
return adjusted;
}

View File

@@ -48,6 +48,9 @@ void gsk_gpu_image_set_flags (GskGpuI
void gsk_gpu_image_get_projection_matrix (GskGpuImage *self,
graphene_matrix_t *out_projection);
GdkColorState * gsk_gpu_image_adjust_color_state (GskGpuImage *self,
GdkColorState *color_state);
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GskGpuImage, g_object_unref)

View File

@@ -1860,7 +1860,6 @@ gsk_gpu_lookup_texture (GskGpuFrame *frame,
GdkColorState **out_image_cs)
{
GskGpuCache *cache;
GdkColorState *image_cs;
GskGpuImage *image;
cache = gsk_gpu_device_get_cache (gsk_gpu_frame_get_device (frame));
@@ -1868,7 +1867,7 @@ gsk_gpu_lookup_texture (GskGpuFrame *frame,
image = gsk_gpu_cache_lookup_texture_image (cache, texture, ccs);
if (image)
{
*out_image_cs = ccs;
*out_image_cs = gdk_color_state_ref (ccs);
return image;
}
@@ -1878,17 +1877,14 @@ gsk_gpu_lookup_texture (GskGpuFrame *frame,
/* Happens ie for oversized textures */
if (image == NULL)
return NULL;
image_cs = gdk_texture_get_color_state (texture);
if (gsk_gpu_image_get_flags (image) & GSK_GPU_IMAGE_SRGB)
{
image_cs = gdk_color_state_get_no_srgb_tf (image_cs);
g_assert (image_cs);
*out_image_cs = NULL;
return NULL;
}
*out_image_cs = image_cs;
*out_image_cs = gsk_gpu_image_adjust_color_state (image,
gdk_texture_get_color_state (texture));
return image;
}
@@ -1922,7 +1918,6 @@ gsk_gpu_node_processor_draw_texture_tiles (GskGpuNodeProcessor *self,
GskGpuCache *cache;
GskGpuDevice *device;
GskGpuImage *tile;
GdkColorState *tile_cs;
GskGpuSampler sampler;
gboolean need_mipmap;
GdkMemoryTexture *memtex;
@@ -1949,6 +1944,8 @@ gsk_gpu_node_processor_draw_texture_tiles (GskGpuNodeProcessor *self,
{
for (x = 0; x < n_width; x++)
{
GdkColorState *tile_cs = NULL;
graphene_rect_t tile_rect = GRAPHENE_RECT_INIT (texture_bounds->origin.x + scaled_tile_width * x,
texture_bounds->origin.y + scaled_tile_height * y,
scaled_tile_width,
@@ -1977,13 +1974,8 @@ gsk_gpu_node_processor_draw_texture_tiles (GskGpuNodeProcessor *self,
goto out;
}
tile_cs = gdk_texture_get_color_state (texture);
if (gsk_gpu_image_get_flags (tile) & GSK_GPU_IMAGE_SRGB)
{
tile_cs = gdk_color_state_get_no_srgb_tf (tile_cs);
g_assert (tile_cs);
}
tile_cs = gsk_gpu_image_adjust_color_state (tile,
gdk_texture_get_color_state (texture));
gsk_gpu_cache_cache_tile (cache, texture, y * n_width + x, tile, tile_cs);
}
@@ -1991,7 +1983,8 @@ gsk_gpu_node_processor_draw_texture_tiles (GskGpuNodeProcessor *self,
(gsk_gpu_image_get_flags (tile) & (GSK_GPU_IMAGE_STRAIGHT_ALPHA | GSK_GPU_IMAGE_CAN_MIPMAP)) != GSK_GPU_IMAGE_CAN_MIPMAP)
{
tile = gsk_gpu_copy_image (self->frame, self->ccs, tile, tile_cs, TRUE);
tile_cs = self->ccs;
gdk_color_state_unref (tile_cs);
tile_cs = gdk_color_state_ref (self->ccs);
gsk_gpu_cache_cache_tile (cache, texture, y * n_width + x, tile, tile_cs);
}
if (need_mipmap && !(gsk_gpu_image_get_flags (tile) & GSK_GPU_IMAGE_MIPMAP))
@@ -2004,6 +1997,7 @@ gsk_gpu_node_processor_draw_texture_tiles (GskGpuNodeProcessor *self,
&tile_rect,
&tile_rect);
gdk_color_state_unref (tile_cs);
g_object_unref (tile);
}
}
@@ -2092,7 +2086,8 @@ gsk_gpu_node_processor_add_texture_node (GskGpuNodeProcessor *self,
!gdk_color_state_equal (image_cs, self->ccs))
{
image = gsk_gpu_copy_image (self->frame, self->ccs, image, image_cs, TRUE);
image_cs = self->ccs;
gdk_color_state_unref (image_cs);
image_cs = gdk_color_state_ref (self->ccs);
gsk_gpu_cache_cache_texture_image (gsk_gpu_device_get_cache (gsk_gpu_frame_get_device (self->frame)),
texture,
image,
@@ -2119,6 +2114,7 @@ gsk_gpu_node_processor_add_texture_node (GskGpuNodeProcessor *self,
&node->bounds);
}
gdk_color_state_unref (image_cs);
g_object_unref (image);
}
@@ -2165,6 +2161,7 @@ gsk_gpu_get_texture_node_as_image (GskGpuFrame *frame,
}
*out_bounds = node->bounds;
gdk_color_state_unref (image_cs);
return image;
}
@@ -2203,6 +2200,7 @@ gsk_gpu_node_processor_add_texture_scale_node (GskGpuNodeProcessor *self,
/* now intersect with actual node bounds */
if (!gsk_rect_intersection (&clip_bounds, &node->bounds, &clip_bounds))
{
gdk_color_state_unref (image_cs);
g_object_unref (image);
return;
}
@@ -2245,7 +2243,8 @@ gsk_gpu_node_processor_add_texture_scale_node (GskGpuNodeProcessor *self,
!gdk_color_state_equal (image_cs, self->ccs))
{
image = gsk_gpu_copy_image (self->frame, self->ccs, image, image_cs, need_mipmap);
image_cs = self->ccs;
gdk_color_state_unref (image_cs);
image_cs = gdk_color_state_ref (self->ccs);
gsk_gpu_cache_cache_texture_image (gsk_gpu_device_get_cache (gsk_gpu_frame_get_device (self->frame)),
texture,
image,
@@ -2265,6 +2264,7 @@ gsk_gpu_node_processor_add_texture_scale_node (GskGpuNodeProcessor *self,
&node->bounds,
});
gdk_color_state_unref (image_cs);
g_object_unref (image);
}

View File

@@ -29,8 +29,14 @@ typedef enum {
GSK_GPU_IMAGE_FILTERABLE = (1 << 6),
GSK_GPU_IMAGE_RENDERABLE = (1 << 7),
GSK_GPU_IMAGE_SRGB = (1 << 8),
GSK_GPU_IMAGE_NARROW_RANGE = (1 << 9),
GSK_GPU_IMAGE_BT601 = (1 << 10),
GSK_GPU_IMAGE_BT709 = (2 << 10),
GSK_GPU_IMAGE_BT2020 = (3 << 10),
} GskGpuImageFlags;
#define GSK_GPU_IMAGE_YUV (GSK_GPU_IMAGE_BT2020)
typedef enum {
GSK_GPU_SAMPLER_DEFAULT,
GSK_GPU_SAMPLER_TRANSPARENT,

View File

@@ -188,6 +188,7 @@ gsk_vulkan_frame_upload_texture (GskGpuFrame *frame,
image = gsk_vulkan_image_new_for_dmabuf (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame)),
gdk_texture_get_width (texture),
gdk_texture_get_height (texture),
gdk_texture_get_color_state (texture),
&dmabuf,
gdk_memory_format_alpha (gdk_texture_get_format (texture)) == GDK_MEMORY_ALPHA_PREMULTIPLIED);
@@ -210,6 +211,7 @@ gsk_vulkan_frame_upload_texture (GskGpuFrame *frame,
image = gsk_vulkan_image_new_for_dmabuf (GSK_VULKAN_DEVICE (gsk_gpu_frame_get_device (frame)),
gdk_texture_get_width (texture),
gdk_texture_get_height (texture),
gdk_texture_get_color_state (texture),
gdk_dmabuf_texture_get_dmabuf (GDK_DMABUF_TEXTURE (texture)),
gdk_memory_format_alpha (gdk_texture_get_format (texture)) == GDK_MEMORY_ALPHA_PREMULTIPLIED);
if (image)

View File

@@ -844,6 +844,7 @@ GskGpuImage *
gsk_vulkan_image_new_for_dmabuf (GskVulkanDevice *device,
gsize width,
gsize height,
GdkColorState *color_state,
const GdkDmabuf *dmabuf,
gboolean premultiplied)
{
@@ -859,6 +860,7 @@ gsk_vulkan_image_new_for_dmabuf (GskVulkanDevice *device,
GdkMemoryFormat format;
GskGpuImageFlags flags;
gboolean is_yuv;
const GdkCicp *cicp;
if (!gsk_vulkan_device_has_feature (device, GDK_VULKAN_FEATURE_DMABUF))
{
@@ -968,13 +970,36 @@ gsk_vulkan_image_new_for_dmabuf (GskVulkanDevice *device,
return NULL;
}
gsk_gpu_image_setup (GSK_GPU_IMAGE (self),
flags |
(gdk_memory_format_alpha (format) == GDK_MEMORY_ALPHA_STRAIGHT ? GSK_GPU_IMAGE_STRAIGHT_ALPHA : 0) |
(is_yuv ? (GSK_GPU_IMAGE_EXTERNAL | GSK_GPU_IMAGE_NO_BLIT) : 0) |
(gsk_component_mapping_is_framebuffer_compatible (&vk_components) ? 0 : GSK_GPU_IMAGE_NO_BLIT),
format,
width, height);
if (is_yuv)
flags |= GSK_GPU_IMAGE_EXTERNAL | GSK_GPU_IMAGE_NO_BLIT;
if (gdk_memory_format_alpha (format) == GDK_MEMORY_ALPHA_STRAIGHT)
flags |= GSK_GPU_IMAGE_STRAIGHT_ALPHA;
if (!gsk_component_mapping_is_framebuffer_compatible (&vk_components))
flags |= GSK_GPU_IMAGE_NO_BLIT;
cicp = gdk_color_state_get_cicp (color_state);
switch (cicp->matrix_coefficients)
{
case 1:
flags |= GSK_GPU_IMAGE_BT709;
break;
case 5:
case 6:
flags |= GSK_GPU_IMAGE_BT601;
break;
case 9:
flags |= GSK_GPU_IMAGE_BT2020;
break;
default:
break;
}
if (cicp->range == GDK_CICP_RANGE_NARROW)
flags |= GSK_GPU_IMAGE_NARROW_RANGE;
gsk_gpu_image_setup (GSK_GPU_IMAGE (self), flags, format, width, height);
self->allocator = gsk_vulkan_device_get_external_allocator (device);
gsk_vulkan_allocator_ref (self->allocator);

View File

@@ -42,6 +42,7 @@ GskGpuImage * gsk_vulkan_image_new_dmabuf (GskVulk
GskGpuImage * gsk_vulkan_image_new_for_dmabuf (GskVulkanDevice *device,
gsize width,
gsize height,
GdkColorState *color_state,
const GdkDmabuf *dmabuf,
gboolean premultiplied);
GdkTexture * gsk_vulkan_image_to_dmabuf_texture (GskVulkanImage *self);

View File

@@ -14,6 +14,8 @@ PASS(2) vec2 _tex_coord;
PASS_FLAT(3) float _opacity;
PASS_FLAT(4) uint _transfer_function;
PASS_FLAT(5) mat3 _mat;
PASS_FLAT(8) mat3 _yuv;
PASS_FLAT(11) uint _range;
#ifdef GSK_VERTEX_SHADER
@@ -22,6 +24,8 @@ IN(1) vec4 in_tex_rect;
IN(2) float in_opacity;
IN(3) uint in_color_primaries;
IN(4) uint in_transfer_function;
IN(5) uint in_matrix_coefficients;
IN(6) uint in_range;
const mat3 identity = mat3(
@@ -90,6 +94,42 @@ const mat3 xyz_to_p3 = mat3(
-0.4027108, 0.0236247, 0.9568845
);
const mat3 rgb_to_bt601 = mat3(
0.299000, -0.168736, 0.500000,
0.587000, -0.331264, -0.418688,
0.114000, 0.500000, -0.081312
);
const mat3 bt601_to_rgb = mat3(
1.000000, 1.000000, 1.000000,
0.000000, -0.344136, 1.772000,
1.402000, -0.714136, 0.000000
);
const mat3 rgb_to_bt709 = mat3(
0.212600, -0.114572, 0.500000,
0.715200, -0.385428, -0.454153,
0.072200, 0.500000, -0.045847
);
const mat3 bt709_to_rgb = mat3(
1.000000, 1.000000, 1.000000,
0.000000, -0.187324, 1.855600,
1.574800, -0.468124, -0.000000
);
const mat3 rgb_to_bt2020 = mat3(
0.262700, -0.139630, 0.500000,
0.678000, -0.360370, -0.459786,
0.059300, 0.500000, -0.040214
);
const mat3 bt2020_to_rgb = mat3(
1.000000, 1.000000, 1.000000,
-0.000000, -0.164553, 1.881400,
1.474600, -0.571353, -0.000000
);
mat3
cicp_to_xyz (uint cp)
{
@@ -121,6 +161,34 @@ cicp_from_xyz (uint cp)
}
mat3
yuv_to_rgb (uint mc)
{
switch (mc)
{
case 0u: return identity;
case 1u: return bt709_to_rgb;
case 5u:
case 6u: return bt601_to_rgb;
case 9u: return bt2020_to_rgb;
}
return mat3(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0);
}
mat3
rgb_to_yuv (uint mc)
{
switch (mc)
{
case 0u: return identity;
case 1u: return rgb_to_bt709;
case 5u:
case 6u: return rgb_to_bt601;
case 9u: return rgb_to_bt2020;
}
return mat3(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0);
}
void
run (out vec2 pos)
{
@@ -133,6 +201,7 @@ run (out vec2 pos)
_tex_coord = rect_get_coord (rect_from_gsk (in_tex_rect), pos);
_opacity = in_opacity;
_transfer_function = in_transfer_function;
_range = in_range;
if (HAS_VARIATION (VARIATION_REVERSE))
{
@@ -141,6 +210,8 @@ run (out vec2 pos)
_mat = cicp_from_xyz (in_color_primaries) * srgb_to_xyz;
else
_mat = cicp_from_xyz (in_color_primaries) * rec2020_to_xyz;
_yuv = rgb_to_yuv (in_matrix_coefficients);
}
else
{
@@ -149,6 +220,8 @@ run (out vec2 pos)
_mat = xyz_to_srgb * cicp_to_xyz (in_color_primaries);
else
_mat = xyz_to_rec2020 * cicp_to_xyz (in_color_primaries);
_yuv = yuv_to_rgb (in_matrix_coefficients);
}
}
@@ -329,6 +402,18 @@ convert_color_from_cicp (vec4 color,
if (from_premul)
color = color_unpremultiply (color);
if (_range == 0u)
{
color.r = (color.r - 16.0/255.0) * 255.0/219.0;
color.g = (color.g - 16.0/255.0) * 255.0/224.0;
color.b = (color.b - 16.0/255.0) * 255.0/224.0;
}
color.g -= 0.5;
color.b -= 0.5;
color.rgb = _yuv * color.rgb;
color.rgb = apply_cicp_eotf (color.rgb, _transfer_function);
color.rgb = _mat * color.rgb;
color.rgb = apply_oetf (color.rgb, to);
@@ -352,6 +437,18 @@ convert_color_to_cicp (vec4 color,
color.rgb = _mat * color.rgb;
color.rgb = apply_cicp_oetf (color.rgb, _transfer_function);
color.rgb = _yuv * color.rgb;
color.g += 0.5;
color.b += 0.5;
if (_range == 0u)
{
color.r = color.r * 219.0/255.0 + 16.0/255.0;
color.g = color.g * 224.0/255.0 + 16.0/255.0;
color.b = color.b * 224.0/255.0 + 16.0/255.0;
}
if (to_premul)
color = color_premultiply (color);

View File

@@ -41,6 +41,7 @@
#include "gtk/css/gtkcssdataurlprivate.h"
#include "gtk/css/gtkcssparserprivate.h"
#include "gtk/css/gtkcssserializerprivate.h"
#include "gdk/loaders/gdkavifprivate.h"
#ifdef CAIRO_HAS_SCRIPT_SURFACE
#include <cairo-script.h>
@@ -216,11 +217,19 @@ parse_texture (GtkCssParser *parser,
if (scheme && g_ascii_strcasecmp (scheme, "data") == 0)
{
GBytes *bytes;
char *mimetype;
bytes = gtk_css_data_url_parse (url, NULL, &error);
bytes = gtk_css_data_url_parse (url, &mimetype, &error);
if (bytes)
{
texture = gdk_texture_new_from_bytes (bytes, &error);
#ifdef HAVE_AVIF
if (strcmp (mimetype, "image/avif") == 0)
texture = gdk_load_avif (bytes, &error);
else
#endif
texture = gdk_texture_new_from_bytes (bytes, &error);
g_free (mimetype);
g_bytes_unref (bytes);
}
else
@@ -3922,12 +3931,27 @@ base64_encode_with_linebreaks (const guchar *data,
return out;
}
#ifdef HAVE_AVIF
static gboolean
texture_should_use_avif (GdkTexture *texture)
{
GdkColorState *color_state;
const GdkCicp *cicp;
color_state = gdk_texture_get_color_state (texture);
cicp = gdk_color_state_get_cicp (color_state);
return cicp->matrix_coefficients != 0;
}
#endif
static void
append_texture_param (Printer *p,
const char *param_name,
GdkTexture *texture)
{
GBytes *bytes;
const char *mimetype;
char *b64;
const char *texture_name;
@@ -3956,26 +3980,38 @@ append_texture_param (Printer *p,
g_hash_table_insert (p->named_textures, texture, new_name);
}
switch (gdk_texture_get_depth (texture))
#ifdef HAVE_AVIF
if (texture_should_use_avif (texture))
{
case GDK_MEMORY_U8:
case GDK_MEMORY_U8_SRGB:
case GDK_MEMORY_U16:
bytes = gdk_texture_save_to_png_bytes (texture);
g_string_append (p->str, "url(\"data:image/png;base64,\\\n");
break;
case GDK_MEMORY_FLOAT16:
case GDK_MEMORY_FLOAT32:
bytes = gdk_texture_save_to_tiff_bytes (texture);
g_string_append (p->str, "url(\"data:image/tiff;base64,\\\n");
break;
case GDK_MEMORY_NONE:
case GDK_N_DEPTHS:
default:
g_assert_not_reached ();
bytes = gdk_save_avif (texture);
mimetype = "image/avif";
}
else
#endif
{
switch (gdk_texture_get_depth (texture))
{
case GDK_MEMORY_U8:
case GDK_MEMORY_U8_SRGB:
case GDK_MEMORY_U16:
bytes = gdk_texture_save_to_png_bytes (texture);
mimetype = "image/png";
break;
case GDK_MEMORY_FLOAT16:
case GDK_MEMORY_FLOAT32:
bytes = gdk_texture_save_to_tiff_bytes (texture);
mimetype = "image/tiff";
break;
case GDK_MEMORY_NONE:
case GDK_N_DEPTHS:
default:
g_assert_not_reached ();
}
}
g_string_append_printf (p->str, "url(\"data:%s;base64,\\\n", mimetype);
b64 = base64_encode_with_linebreaks (g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes));

View File

@@ -417,6 +417,9 @@ pixbuf_dep = dependency('gdk-pixbuf-2.0', version: gdk_pixbuf_req,
png_dep = dependency('libpng', 'png')
tiff_dep = dependency('libtiff-4', 'tiff')
jpeg_dep = dependency('libjpeg', 'jpeg')
avif_dep = dependency('libavif', required: get_option('avif'))
cdata.set('HAVE_AVIF', avif_dep.found())
epoxy_dep = dependency('epoxy', version: epoxy_req)
xkbdep = dependency('xkbcommon', version: xkbcommon_req, required: wayland_enabled)
@@ -955,6 +958,7 @@ summary('Cloud support', cloudproviders_dep.found(), section: 'Features')
summary('Sysprof support', libsysprof_capture_dep.found(), section: 'Features')
summary('Colord support', colord_dep.found(), section: 'Features')
summary('Tracker support', tracker3_dep.found(), section: 'Features')
summary('AVIF support', avif_dep.found(), section: 'Features')
summary('Compiler', cc.get_id(), section: 'Toolchain')
summary('Linker', cc.get_linker_id(), section: 'Toolchain')

View File

@@ -52,6 +52,11 @@ option('print-cups',
# Optional features
option('avif',
type: 'feature',
value: 'disabled',
description: 'Enable support for AVIF images, for GTK development')
option('vulkan',
type: 'feature',
value: 'enabled',

View File

@@ -24,6 +24,8 @@
#include "gtkgstpaintableprivate.h"
#include "gdk/gdkdmabuffourccprivate.h"
#if GST_GL_HAVE_WINDOW_X11 && (GST_GL_HAVE_PLATFORM_GLX || GST_GL_HAVE_PLATFORM_EGL) && defined (GDK_WINDOWING_X11)
#define HAVE_GST_X11_SUPPORT
#include <gdk/x11/gdkx.h>
@@ -156,9 +158,72 @@ add_drm_formats_and_modifiers (GstCaps *caps,
&dmabuf_list);
}
#ifdef HAVE_GSTREAMER_DRM
static inline gboolean
dmabuf_fourcc_is_yuv (guint32 fourcc)
{
switch (fourcc)
{
case DRM_FORMAT_YUYV:
case DRM_FORMAT_YVYU:
case DRM_FORMAT_VYUY:
case DRM_FORMAT_UYVY:
case DRM_FORMAT_AYUV:
case DRM_FORMAT_AVUY8888:
case DRM_FORMAT_XYUV8888:
case DRM_FORMAT_XVUY8888:
case DRM_FORMAT_VUY888:
case DRM_FORMAT_VUY101010:
case DRM_FORMAT_Y210:
case DRM_FORMAT_Y212:
case DRM_FORMAT_Y216:
case DRM_FORMAT_Y410:
case DRM_FORMAT_Y412:
case DRM_FORMAT_Y416:
case DRM_FORMAT_XVYU2101010:
case DRM_FORMAT_XVYU12_16161616:
case DRM_FORMAT_XVYU16161616:
case DRM_FORMAT_Y0L0:
case DRM_FORMAT_X0L0:
case DRM_FORMAT_Y0L2:
case DRM_FORMAT_X0L2:
case DRM_FORMAT_YUV420_8BIT:
case DRM_FORMAT_YUV420_10BIT:
case DRM_FORMAT_NV12:
case DRM_FORMAT_NV21:
case DRM_FORMAT_NV16:
case DRM_FORMAT_NV61:
case DRM_FORMAT_NV24:
case DRM_FORMAT_NV42:
case DRM_FORMAT_NV15:
case DRM_FORMAT_P210:
case DRM_FORMAT_P010:
case DRM_FORMAT_P012:
case DRM_FORMAT_P016:
case DRM_FORMAT_P030:
case DRM_FORMAT_Q410:
case DRM_FORMAT_Q401:
case DRM_FORMAT_YUV410:
case DRM_FORMAT_YVU410:
case DRM_FORMAT_YUV411:
case DRM_FORMAT_YVU411:
case DRM_FORMAT_YUV420:
case DRM_FORMAT_YVU420:
case DRM_FORMAT_YUV422:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_YUV444:
case DRM_FORMAT_YVU444:
return TRUE;
default:
return FALSE;
}
}
#endif
static GdkColorState *
gtk_gst_color_state_from_colorimetry (GtkGstSink *self,
const GstVideoColorimetry *colorimetry)
const GstVideoColorimetry *colorimetry,
gconstpointer drm)
{
GdkCicpParams *params;
GdkColorState *color_state;
@@ -176,18 +241,40 @@ gtk_gst_color_state_from_colorimetry (GtkGstSink *self,
else
gdk_cicp_params_set_transfer_function (params, gst_video_transfer_function_to_iso (colorimetry->transfer));
#if 0
gdk_cicp_params_set_range (params, colorimetry->range == GST_VIDEO_COLOR_RANGE_16_235 ? GDK_CICP_RANGE_NARROW : GDK_CICP_RANGE_FULL);
if (colorimetry->matrix == GST_VIDEO_COLOR_MATRIX_UNKNOWN)
gdk_cicp_params_set_matrix_coefficients (params, 6);
{
gdk_cicp_params_set_matrix_coefficients (params, 0);
#ifdef HAVE_GSTREAMER_DRM
const GstVideoInfoDmaDrm *drm_info = drm;
if (drm_info)
{
if (dmabuf_fourcc_is_yuv (drm_info->drm_fourcc))
{
GST_DEBUG_OBJECT (self, "Assuming fourcc %.*s is yuv", 4, (char *)&drm_info->drm_fourcc);
gdk_cicp_params_set_matrix_coefficients (params, 6);
gdk_cicp_params_set_range (params, GDK_CICP_RANGE_NARROW);
}
}
#endif
}
else
gdk_cicp_params_set_matrix_coefficients (params, gst_video_color_matrix_to_iso (colorimetry->matrix));
gdk_cicp_params_set_range (params, colorimetry->range == GST_VIDEO_COLOR_RANGE_0_255 ? GDK_CICP_RANGE_FULL : GDK_CICP_RANGE_NARROW);
#else
gdk_cicp_params_set_matrix_coefficients (params, 0);
gdk_cicp_params_set_range (params, GDK_CICP_RANGE_FULL);
#endif
color_state = gdk_cicp_params_build_color_state (params, &error);
GST_DEBUG_OBJECT (self, "cicp received: %u/%u/%u/%u",
gst_video_color_primaries_to_iso (colorimetry->primaries),
gst_video_transfer_function_to_iso (colorimetry->transfer),
gst_video_color_matrix_to_iso (colorimetry->matrix),
colorimetry->range == GST_VIDEO_COLOR_RANGE_0_255);
GST_DEBUG_OBJECT (self, "cicp used: %u/%u/%u/%u",
gdk_cicp_params_get_color_primaries (params),
gdk_cicp_params_get_transfer_function (params),
gdk_cicp_params_get_matrix_coefficients (params),
gdk_cicp_params_get_range (params));
g_object_unref (params);
if (color_state == NULL)
@@ -266,7 +353,14 @@ gtk_gst_sink_set_caps (GstBaseSink *bsink,
}
g_clear_pointer (&self->color_state, gdk_color_state_unref);
self->color_state = gtk_gst_color_state_from_colorimetry (self, &self->v_info.colorimetry);
self->color_state = gtk_gst_color_state_from_colorimetry (self,
&self->v_info.colorimetry,
#ifdef HAVE_GSTREAMER_DRM
&self->drm_info
#else
NULL
#endif
);
if (self->color_state == NULL)
return FALSE;

View File

@@ -49,6 +49,9 @@ static MatrixTest matrices[] = {
{ "ntsc", ntsc_to_xyz, xyz_to_ntsc },
{ "p3", p3_to_xyz, xyz_to_p3 },
{ "srgb<>rec2020", rec2020_to_srgb, srgb_to_rec2020 },
{ "bt601", bt601_to_rgb, rgb_to_bt601 },
{ "bt709", bt709_to_rgb, rgb_to_bt709 },
{ "bt2020", bt2020_to_rgb, rgb_to_bt2020 },
};
#define IDX(i,j) 3*i+j

View File

@@ -5,6 +5,7 @@
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdkdmabuffourccprivate.h>
#include <gdk/gdkdmabuftextureprivate.h>
#include <gdk/gdkdmabufeglprivate.h>
static cairo_surface_t *
make_surface (int width,
@@ -191,6 +192,8 @@ test_dmabuf_import (void)
GdkGLTextureBuilder *builder;
guchar *data;
gboolean external;
GdkColorState *color_state;
int color_space, range;
display = gdk_display_get_default ();
if (!gdk_display_prepare_gl (display, &error))
@@ -229,7 +232,9 @@ test_dmabuf_import (void)
g_assert_no_error (error);
dmabuf2 = gdk_dmabuf_texture_get_dmabuf (GDK_DMABUF_TEXTURE (texture));
texture_id2 = gdk_gl_context_import_dmabuf (context2, 64, 64, dmabuf2, &external);
color_state = gdk_texture_get_color_state (texture);
gdk_dmabuf_get_egl_yuv_hints (dmabuf2, color_state, &color_space, &range);
texture_id2 = gdk_gl_context_import_dmabuf (context2, 64, 64, dmabuf2, color_space, range, &external);
g_assert_cmpint (texture_id2, !=, 0);
g_assert_false (external);

View File

@@ -6,6 +6,12 @@ clipboard_client = executable('clipboard-client',
install: false,
)
extra_yuv_sources = []
if avif_dep.found()
extra_yuv_sources += ['../../gdk/loaders/gdkavif.c']
endif
tests = [
{ 'name': 'array' },
{ 'name': 'cairo' },
@@ -24,8 +30,12 @@ tests = [
{ 'name': 'texture-threads' },
{ 'name': 'toplevellayout' },
{ 'name': 'popuplayout' },
{ 'name': 'yuv',
'sources': [ 'yuv.c', '../reftests/reftest-compare.c' ] + extra_yuv_sources,
},
]
if x11_enabled
tests += [
{ 'name': 'display' },
@@ -36,7 +46,7 @@ endif
foreach t : tests
test_name = t.get('name')
test_exe = executable(test_name,
sources: '@0@.c'.format(test_name),
sources: ['@0@.c'.format(test_name)] + t.get('sources', []),
c_args: common_cflags,
dependencies: libgtk_dep,
install: false,
@@ -76,7 +86,7 @@ endif
foreach t : internal_tests
test_name = t.get('name')
test_exe = executable(test_name,
sources: '@0@.c'.format(test_name),
sources: ['@0@.c'.format(test_name)] + t.get('sources', []),
c_args: common_cflags + ['-DGTK_COMPILATION'],
dependencies: libgtk_static_dep,
install: false,

Binary file not shown.

After

Width:  |  Height:  |  Size: 999 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 999 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 996 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1000 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1000 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 B

236
testsuite/gdk/yuv.c Normal file
View File

@@ -0,0 +1,236 @@
#include "config.h"
#include <gtk/gtk.h>
#include "gdk/loaders/gdkavifprivate.h"
#include "../reftests/reftest-compare.h"
static float
image_distance (const guchar *data,
gsize stride,
const guchar *data2,
gsize stride2,
gsize width,
gsize height)
{
float dist = 0;
gsize imax = 0, jmax = 0;
float r1 = 0, g1 = 0, b1 = 0, a1 = 0;
float r2 = 0, g2 = 0, b2 = 0, a2 = 0;
for (gsize i = 0; i < height; i++)
{
const float *p = (const float *) (data + i * stride);
const float *p2 = (const float *) (data2 + i * stride2);
for (gsize j = 0; j < width; j++)
{
float dr, dg, db, da;
dr = p[4 * j + 0] - p2[4 * j + 0];
dg = p[4 * j + 1] - p2[4 * j + 1];
db = p[4 * j + 2] - p2[4 * j + 2];
da = p[4 * j + 3] - p2[4 * j + 3];
float d = dr * dr + dg * dg + db * db + da * da;
if (d > dist)
{
dist = d;
imax = i;
jmax = j;
r1 = p[4 * j + 0];
g1 = p[4 * j + 1];
b1 = p[4 * j + 2];
a1 = p[4 * j + 3];
r2 = p2[4 * j + 0];
g2 = p2[4 * j + 1];
b2 = p2[4 * j + 2];
a2 = p2[4 * j + 3];
}
}
}
if (g_test_verbose() &&
dist > 0)
{
g_print ("worst pixel %" G_GSIZE_FORMAT " %" G_GSIZE_FORMAT ": %f %f %f %f vs %f %f %f %f %f\n",
imax, jmax, r1, g1, b1, a1, r2, g2, b2, a2, sqrt (dist));
}
return sqrt (dist);
}
static void
assert_texture_equal (GdkTexture *t1,
GdkTexture *t2)
{
int width;
int height;
int stride;
guchar *d1;
guchar *d2;
GdkTextureDownloader *downloader;
width = gdk_texture_get_width (t1);
height = gdk_texture_get_height (t1);
g_assert_cmpint (width, ==, gdk_texture_get_width (t2));
g_assert_cmpint (height, ==, gdk_texture_get_height (t2));
stride = 4 * width * sizeof (float);
d1 = g_malloc (stride * height);
d2 = g_malloc (stride * height);
downloader = gdk_texture_downloader_new (t1);
gdk_texture_downloader_set_format (downloader, GDK_MEMORY_R32G32B32A32_FLOAT);
gdk_texture_downloader_set_color_state (downloader, gdk_color_state_get_srgb ());
gdk_texture_downloader_download_into (downloader, d1, stride);
gdk_texture_downloader_free (downloader);
downloader = gdk_texture_downloader_new (t2);
gdk_texture_downloader_set_format (downloader, GDK_MEMORY_R32G32B32A32_FLOAT);
gdk_texture_downloader_set_color_state (downloader, gdk_color_state_get_srgb ());
gdk_texture_downloader_download_into (downloader, d2, stride);
gdk_texture_downloader_free (downloader);
#if 0
GdkTexture *diff;
diff = reftest_compare_textures (t1, t2);
if (diff)
{
gdk_texture_save_to_png (t1, "pattern1.png");
gdk_texture_save_to_png (t2, "pattern2.png");
gdk_texture_save_to_png (diff, "pattern.diff.png");
g_object_unref (diff);
g_test_fail ();
}
#endif
if (image_distance (d1, stride, d2, stride, width, height) > 0.01)
{
g_test_fail ();
return;
}
//g_assert_cmpmem (d1, stride * height, d2, stride * height);
g_free (d1);
g_free (d2);
}
static char *
get_reference (const char *filename)
{
char *p;
char *basename;
char *reference;
p = strchr (filename, '-');
if (!p)
return NULL;
basename = g_strndup (filename, p - filename);
reference = g_strconcat (basename, ".png", NULL);
g_free (basename);
return reference;
}
static void
test_load_image (gconstpointer testdata)
{
const char *filename = testdata;
GdkTexture *texture = NULL;
GdkTexture *texture2;
char *path;
char *reference;
char *path2;
char *data;
gsize size;
GBytes *bytes;
GError *error = NULL;
#ifndef HAVE_AVIF
g_test_skip ("built without avif support");
return;
#endif
reference = get_reference (filename);
if (!reference)
{
g_test_skip ("no reference image");
return;
}
path = g_test_build_filename (G_TEST_DIST, "yuv-images", filename, NULL);
g_file_get_contents (path, &data, &size, &error);
g_assert_no_error (error);
bytes = g_bytes_new_take (data, size);
#ifdef HAVE_AVIF
texture = gdk_load_avif (bytes, &error);
#endif
g_bytes_unref (bytes);
if (texture == NULL)
{
g_test_skip_printf ("unsupported format: %s", error->message);
g_error_free (error);
return;
}
if (!GDK_IS_DMABUF_TEXTURE (texture))
{
g_test_message ("No dmabuf texture. /dev/udmabuf not available?");
}
path2 = g_test_build_filename (G_TEST_DIST, "yuv-images", reference, NULL);
texture2 = gdk_texture_new_from_filename (path2, &error);
g_assert_no_error (error);
g_assert_true (GDK_IS_TEXTURE (texture2));
assert_texture_equal (texture, texture2);
g_object_unref (texture);
g_object_unref (texture2);
g_free (path);
g_free (path2);
g_free (reference);
}
int
main (int argc, char *argv[])
{
char *path;
GDir *dir;
const char *name;
GError *error = NULL;
gtk_init ();
(g_test_init) (&argc, &argv, NULL);
path = g_test_build_filename (G_TEST_DIST, "yuv-images", NULL);
dir = g_dir_open (path, 0, &error);
g_assert_no_error (error);
g_free (path);
while ((name = g_dir_read_name (dir)) != NULL)
{
if (!g_str_has_suffix (name, ".png"))
{
char *test = g_strconcat ("/yuv/load/", name, NULL);
g_test_add_data_func (test, name, test_load_image);
g_free (test);
}
}
return g_test_run ();
}

View File

@@ -79,10 +79,10 @@ do_compare (int *argc,
for (int i = 0; i < 2; i++)
{
texture[i] = gdk_texture_new_from_filename (filenames[i], &error);
texture[i] = load_image_file (filenames[i]);
if (texture[i] == NULL)
{
g_printerr (_("Failed to load %s: %s\n"), filenames[i], error->message);
g_printerr (_("Failed to load %s\n"), filenames[i]);
exit (1);
}
}

View File

@@ -31,10 +31,10 @@
static void
save_image (const char *filename,
const char *output,
GdkMemoryFormat format,
GdkColorState *color_state)
convert_image (const char *filename,
const char *output,
GdkMemoryFormat format,
GdkColorState *color_state)
{
GdkTexture *orig;
GdkTextureDownloader *downloader;
@@ -61,10 +61,7 @@ save_image (const char *filename,
texture = gdk_memory_texture_builder_build (builder);
if (g_str_has_suffix (output, ".tiff"))
gdk_texture_save_to_tiff (texture, output);
else
gdk_texture_save_to_png (texture, output);
save_texture (texture, output);
g_object_unref (texture);
g_bytes_unref (bytes);
@@ -173,7 +170,7 @@ do_convert (int *argc,
if (!color_state)
color_state = gdk_color_state_get_srgb ();
save_image (filenames[0], filenames[1], format, color_state);
convert_image (filenames[0], filenames[1], format, color_state);
g_strfreev (filenames);
}

View File

@@ -48,12 +48,23 @@ static void
file_info (const char *filename)
{
GdkTexture *texture;
char *name;
char *cicp;
texture = load_image_file (filename);
g_print ("%s %dx%d\n", _("Size:"), gdk_texture_get_width (texture), gdk_texture_get_height (texture));
g_print ("%s %s\n", _("Format:"), get_format_name (gdk_texture_get_format (texture)));
g_print ("%s %s\n", _("Color state:"), get_color_state_name (gdk_texture_get_color_state (texture)));
name = get_color_state_name (gdk_texture_get_color_state (texture));
cicp = get_color_state_cicp (gdk_texture_get_color_state (texture));
if (name && cicp)
g_print ("%s %s (cicp %s)\n", _("Color state:"), name, cicp);
else if (cicp)
g_print ("%s cicp %s\n", _("Color state:"), cicp);
else
g_print ("%s %s\n", _("Color state:"), _("unknown"));
g_object_unref (texture);
}

View File

@@ -60,10 +60,7 @@ relabel_image (const char *filename,
texture = gdk_memory_texture_builder_build (builder);
if (g_str_has_suffix (output, ".tiff"))
gdk_texture_save_to_tiff (texture, output);
else
gdk_texture_save_to_png (texture, output);
save_texture (texture, output);
g_object_unref (texture);
g_bytes_unref (bytes);

View File

@@ -28,15 +28,29 @@
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include "gtk-image-tool.h"
#include "gdk/loaders/gdkavifprivate.h"
GdkTexture *
load_image_file (const char *filename)
{
GError *error = NULL;
GdkTexture *texture;
GdkTexture *texture = NULL;
char *data;
gsize size;
GBytes *bytes;
if (g_file_get_contents (filename, &data, &size, &error))
{
bytes = g_bytes_new_take (data, size);
#ifdef HAVE_AVIF
if (gdk_is_avif (bytes))
texture = gdk_load_avif (bytes, &error);
else
#endif
texture = gdk_texture_new_from_bytes (bytes, &error);
}
texture = gdk_texture_new_from_filename (filename, &error);
if (!texture)
{
g_printerr ("%s\n", error->message);
@@ -44,9 +58,39 @@ load_image_file (const char *filename)
exit (1);
}
g_bytes_unref (bytes);
return texture;
}
gboolean
save_texture (GdkTexture *texture,
const char *filename)
{
if (g_str_has_suffix (filename, ".png"))
return gdk_texture_save_to_png (texture, filename);
else if (g_str_has_suffix (filename, ".tiff"))
return gdk_texture_save_to_tiff (texture, filename);
#ifdef HAVE_AVIF
else if (g_str_has_suffix (filename, ".avif"))
{
GBytes *bytes;
gboolean result;
bytes = gdk_save_avif (texture);
result = g_file_set_contents (filename,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes),
NULL);
g_bytes_unref (bytes);
return result;
}
#endif
return FALSE;
}
gboolean
find_format_by_name (const char *name,
GdkMemoryFormat *format)
@@ -149,6 +193,17 @@ find_color_state_by_name (const char *name)
gdk_cicp_params_set_matrix_coefficients (params, 6);
gdk_cicp_params_set_range (params, GDK_CICP_RANGE_NARROW);
color_state = gdk_cicp_params_build_color_state (params, &error);
}
else if (g_strcmp0 (name, "jpeg") == 0)
{
params = gdk_cicp_params_new ();
gdk_cicp_params_set_color_primaries (params, 1);
gdk_cicp_params_set_transfer_function (params, 13);
gdk_cicp_params_set_matrix_coefficients (params, 6);
gdk_cicp_params_set_range (params, GDK_CICP_RANGE_FULL);
color_state = gdk_cicp_params_build_color_state (params, &error);
}
else if (g_strcmp0 (name, "bt709") == 0)
@@ -181,13 +236,34 @@ get_color_state_names (void)
static const char *names[] = {
"srgb", "srgb-linear", "display-p3", "rec2020",
"rec2100-pq", "rec2100-linear", "rec2100-hlg",
"yuv", "bt601", "bt709",
"yuv", "jpeg", "bt601", "bt709",
NULL,
};
return g_strdupv ((char **) names);
}
char *
get_color_state_cicp (GdkColorState *color_state)
{
GdkCicpParams *params;
char *str = NULL;
params = gdk_color_state_create_cicp_params (color_state);
if (params)
{
str = g_strdup_printf ("%u/%u/%u/%u",
gdk_cicp_params_get_color_primaries (params),
gdk_cicp_params_get_transfer_function (params),
gdk_cicp_params_get_matrix_coefficients (params),
gdk_cicp_params_get_range (params));
g_object_unref (params);
}
return str;
}
char *
get_color_state_name (GdkColorState *color_state)
{

View File

@@ -8,7 +8,8 @@ void do_relabel (int *argc, const char ***argv);
void do_show (int *argc, const char ***argv);
GdkTexture * load_image_file (const char *filename);
gboolean save_texture (GdkTexture *texture,
const char *filename);
gboolean find_format_by_name (const char *name,
GdkMemoryFormat *format);
@@ -17,6 +18,7 @@ GdkColorState * find_color_state_by_name (const char *name);
char ** get_color_state_names (void);
char * get_color_state_name (GdkColorState *color_state);
char * get_color_state_cicp (GdkColorState *color_state);
GdkColorState *parse_cicp_tuple (const char *cicp_tuple,
GError **error);

View File

@@ -22,6 +22,12 @@ if win32_enabled
extra_update_icon_cache_objs = import('windows').compile_resources(uac_rc)
endif
extra_image_tool_sources = []
if avif_dep.found()
extra_image_tool_sources = ['../gdk/loaders/gdkavif.c']
endif
gtk_tools = [
['gtk4-path-tool', ['gtk-path-tool.c',
'gtk-path-tool-decompose.c',
@@ -56,7 +62,8 @@ gtk_tools = [
'gtk-image-tool-relabel.c',
'gtk-image-tool-show.c',
'gtk-image-tool-utils.c',
'../testsuite/reftests/reftest-compare.c'], [libgtk_dep] ],
'../testsuite/reftests/reftest-compare.c'] + extra_image_tool_sources,
[libgtk_dep] ],
['gtk4-update-icon-cache', ['updateiconcache.c', '../gtk/gtkiconcachevalidator.c' ] + extra_update_icon_cache_objs, [ libgtk_dep ] ],
['gtk4-encode-symbolic-svg', ['encodesymbolic.c'], [ libgtk_static_dep ] ],
]
@@ -73,7 +80,7 @@ foreach tool: gtk_tools
exe = executable(tool_name,
sources: tool_srcs,
include_directories: [confinc],
c_args: common_cflags + [ '-DBUILD_TOOLS' ],
c_args: common_cflags + [ '-DBUILD_TOOLS', '-DAVIF_DEBUG' ],
dependencies: tool_deps,
install: true,
)