Compare commits

...

51 Commits

Author SHA1 Message Date
Matthias Clasen
6cb3300009 Add a test for color conversion 2022-09-30 20:57:52 -04:00
Matthias Clasen
fde2fefbcd testsuite: Compare textures properly
When compoaring textures, we now need to look
at the colorspace as well.
2022-09-30 14:42:03 -04:00
Matthias Clasen
b6eb23afa7 widget-factory: Add an hdr image example
This uses the heif loader to show an image in
the 'color profile' notebook. The current image
isn't a very good HDR example, and should be
replaced by a more suitable image.
2022-09-30 14:42:03 -04:00
Matthias Clasen
17b7e9819b Add a quick-and-dirty heif loading test
This uses libheif to load an image and produce
a texture. If the image has more than 8 bits of
chroma or luma, the texture will be 16bit.
2022-09-30 14:42:03 -04:00
Matthias Clasen
966a150d19 Add libheif to the build 2022-09-30 14:42:03 -04:00
Matthias Clasen
1b83c44994 wip: Add a cicp color space implementation
This is a bit preliminary. It can't handle
HDR formats yet.
2022-09-30 14:42:03 -04:00
Matthias Clasen
6e75db713c wip: update testsuite for changed compositing 2022-09-30 14:42:03 -04:00
Matthias Clasen
82b45004c2 gsk: Add color space in gsk_renderer_render_texture
Make gsk_renderer_render_texture produce a texture
that has the correct color space attached. This
fixes the rendering of node files in the node editor.
2022-09-30 14:42:03 -04:00
Matthias Clasen
31b9e8dca6 gdk: Introduce GdkColor
GdkColor represents a color in the real world, by combining a color
profile, an alpha value and N component values.

gsk_render_node_draw() has been ported to use GdkColor when rendering,
which makes it so the rendering happens in a color-managed way when a
color profile has been attached to the target.
2022-09-30 14:42:03 -04:00
Matthias Clasen
623cdaf485 media: Use the new GL texture constructor
Use GdkGLTextureFlags in the gstreamer backend
to tell gsk that the buffers are unpremultiplied,
non-linear sRGB.
2022-09-30 14:42:03 -04:00
Matthias Clasen
77280d258a glarea: Use the new GL texture constructor
This lets us avoid doing the flipping in snapshot.
We can just tell gsk to do it for us.
2022-09-30 14:42:03 -04:00
Matthias Clasen
80bf2336c9 gsk: Use GL texture information
Use the flags and color space information from
GL textures to figure out what conversions we need
to do on them.
2022-09-30 14:42:03 -04:00
Matthias Clasen
55179c450b API: Add a new constructor for GL textures
Add a constructor that lets us provide more information
about the content of a GL texture, such as the color
profile, and whether it is premultiplied or flipped.
2022-09-30 14:42:03 -04:00
Matthias Clasen
f7268a5c52 gsk: Support flipping gl textures
Add upside-down flipping to the list of supported
conversions when handling GL textures.
2022-09-30 14:42:03 -04:00
Matthias Clasen
0e0bb92ff9 gsk: Do texture conversions in shaders
Do linearization and premultiplication of textures
in a shader after uploading the unconverted data.
This moves move work from the cpu to the gpu.

Conversions between other color spaces are still
done with lcms on the cpu.
2022-09-30 14:42:03 -04:00
Matthias Clasen
616a9e1804 gsk: Refactor a bit 2022-09-30 14:42:03 -04:00
Matthias Clasen
7371840772 gsk: Fix caching of textures 2022-09-30 14:42:03 -04:00
Matthias Clasen
ca062df561 cairo: Use stem darkening for glyphs
This involves tweaking a freetype driver property
directly, since cairo font options don't cover this.

So the code is a bit ugly, but it does make text
appear darker.
2022-09-30 14:42:03 -04:00
Matthias Clasen
b09c642c98 gsk: Use stem darkening for glyphs
This involves tweaking a freetype driver property
directly, since cairo font options don't cover this.

So the code is a bit ugly, but it does make text
appear darker.
2022-09-30 14:42:03 -04:00
Matthias Clasen
830892e1b7 gsk: Linearize gl textures
GL textures are typically in non-linear sRGB,
and in that case, we need to linearize them.
2022-09-30 14:42:03 -04:00
Matthias Clasen
fda050e4fa gsk: Linearize colors too
Convert all the GdkRGBA colors that we have
in render nodes to linear sRGB before using
them. This brings the rendering back in line
with what cairo produces, except for the video.
2022-09-30 14:42:03 -04:00
Matthias Clasen
7e91c156fe gsk: Apply gamma to frames
Use a shader to go from linear sRGB to sRGB when
we are done with a frame.
2022-09-30 14:42:03 -04:00
Matthias Clasen
d9e6e92a64 gsk: Upload textures in linear sRGB
We want to do compositing in a linear color space,
so convert textures to linear sRGB before uploading.

Currently, this causes images to come out dark, since
we don't have a way to tell the compositor that our
framebuffer is linear, so it assumes a gamma that we
don't provide.
2022-09-30 14:42:03 -04:00
Benjamin Otte
ff78a913d7 gdk: Add GDK_DEBUG=srgb
Disables gdk_surface_set_color_profile() for backends and forces
sRGB.

This does not change any GSK renderers, it just turns off any backends
trying to hand us color profiles.
2022-09-30 14:42:03 -04:00
Benjamin Otte
91ffa97874 Add crude color management impl for cairo 2022-09-30 14:42:03 -04:00
Benjamin Otte
3ce7556235 x11: Implement support for color profiles
Stole the implementation from eog.

This doesn't yet update the profile when it changes though.
2022-09-30 14:42:03 -04:00
Matthias Clasen
2880a9c80f Support color space in pixbufs
When creating a GdkTexture from a GdkPixbuf,
see if it has an icc profile attached, and if
so, use it.
2022-09-30 14:42:03 -04:00
Benjamin Otte
f0e2e58939 widget-factory: Add tests for loading color profiles
I figured out how to generate them, yay:

  cmsHPROFILE lcms_profile;
  cmsToneCurve *curve[3];

  curve[0] = cmsBuildParametricToneCurve (NULL, 4, (double[5]) { 1.0, 0, 0, 0, 0 });
  curve[1] = cmsBuildParametricToneCurve (NULL, 4, (double[5]) { 2.4, 255.0 / 15 / 1.055, 0.055 / 1.055, 0, 15./255 });
  curve[2] = cmsBuildGamma (NULL, 2.4);
  lcms_profile = cmsCreateRGBProfile (&(cmsCIExyY) {
                                        0.3127, 0.3290, 1.0
                                      },
                                      &(cmsCIExyYTRIPLE) {
                                        { 0.6400, 0.3300, 1.0 },
                                        { 0.3000, 0.6000, 1.0 },
                                        { 0.1500, 0.0600, 1.0 }
                                      },
                                      curve);

  cmsSaveProfileToFile (lcms_profile, "foo.icc");

  cmsFreeToneCurveTriple (curve);
  cmsCloseProfile (lcms_profile);
2022-09-30 14:42:03 -04:00
Matthias Clasen
4137f26f41 lcmscolorspace: Implement a global transform cache
Speeds up things by a factor of 10x, so seems like a good idea.

The cache is never purges, we might want to fix that.
2022-09-30 14:42:03 -04:00
Benjamin Otte
b4116cbbb2 memoryformat: Optimize more
Make the formats allow converting to an lcms-compatible format and
then use a cmsTransform to convert between them.

Note that mixing RGBA and non-RGBA formats doesn't seem to work, so we
force all our RGB-only formats to convert to a RGBA format.
2022-09-30 14:42:03 -04:00
Benjamin Otte
c5909b16ad memoryformat: Do some gdk_memory_convert() massaging
* Add name support to the formats
* Add profiler mark to gdk_memory_convert()
* Split up the different branches of conversion
* Use memcpy for straight copies
2022-09-30 14:42:03 -04:00
Matthias Clasen
d2f9acf3b0 gsk: Avoid cairo in icon upload
Use the new memory texture magic
2022-09-30 14:42:03 -04:00
Matthias Clasen
3214e5a703 tiff: Add color space support
Apply an embedded icc profile when loading tiff
images, and save associated icc profile information
when saving tiff images.
2022-09-30 14:42:03 -04:00
Benjamin Otte
9ceaf3445d png: Handle color spaces 2022-09-30 14:42:02 -04:00
Matthias Clasen
8fd866ba2e gdk: Take a color space in gdk_memory_texture_from_texture
This will let us do profile conversions at the
same time.

Update all callers.
2022-09-30 14:42:02 -04:00
Benjamin Otte
f1e3ccf0d4 gdk: Take a color space in gdk_texture_download_surface
This way, the resulting surface can contain the pixels
in the desired color space.
2022-09-30 14:42:02 -04:00
Matthias Clasen
03c9e25377 memoryformat: Take a color space when converting 2022-09-30 14:42:02 -04:00
Benjamin Otte
d161830d4c API: Add color space get/set for cairo
Add a centralized place to attach color space to.

Nothing uses that information yet, but all the
backends do set it.
2022-09-30 14:42:02 -04:00
Benjamin Otte
d546785f90 API: Add GdkSurface::color-space
Unused so far, but there's a private setter for backends and a public
readable property for code.
2022-09-30 14:42:02 -04:00
Matthias Clasen
e9315dbcf8 gtk-demo: Add a color space demo
This is using test images from http://displaycal.net/.
2022-09-30 14:42:02 -04:00
Benjamin Otte
69403eb66c widget-factory: Add gradient rendering test
Test how GTK draws gradients, by adding an sRGB and a linear colorspace
gradient and draw one with CSS.

Have a look which one (if any) matches.
2022-09-30 14:42:02 -04:00
Benjamin Otte
23fdeb9f20 jpeg: Add color space support 2022-09-30 14:42:02 -04:00
Benjamin Otte
157ad464d8 API: Add gdk_memory_texture_new_with_color_space()
A version of gdk_memory_texture_new() that allows passing
a color space. The old version assumes sRGB.
2022-09-30 14:42:02 -04:00
Benjamin Otte
e675b54df7 API: Add a GdkTexture::color-space property
Returns the associated color space. For now, this is always sRGB.
2022-09-30 14:42:02 -04:00
Matthias Clasen
9b824abddd Add some colorspace tests
This is very minimal, but it is enough to do
a very basic check of the gdk_color_space_equal
implementation.
2022-09-30 14:42:02 -04:00
Benjamin Otte
28f670d5fa gdk: Add GdkLcmsColorSpace
This is a colorspace implementation using LCMS to implement support
for random colorspaces from ICC profiles.
2022-09-30 14:41:31 -04:00
Benjamin Otte
28d70d17e0 Add mutter and lcms2-devel to the Fedora image
mutter will be needed to get a WM for the x11 testsuite.

lcms2 is needed for upcoming HDR work, so having it available in
varioius branches is neat.
2022-09-30 14:41:31 -04:00
Matthias Clasen
b286901f2b ci: Update dependencies for msys
Add libpng, libjpeg-turbo, libtiff and lcms2.
2022-09-30 14:41:31 -04:00
Matthias Clasen
588a7212c7 Add an lcms2 subproject
Since lcms2 is using autotools, this uses the
experimental 'external project' module of meson,
and adds a minimal meson.build file to lcms2.

It seems to work.
2022-09-30 14:41:31 -04:00
Benjamin Otte
6979811b0e cms: Add lcms to the build 2022-09-30 14:41:31 -04:00
Benjamin Otte
87fcac7508 gdk: Add GdkColorSpace
The code doesn't do anything yet, this is just the boilerplate.
2022-09-30 14:41:31 -04:00
112 changed files with 5036 additions and 710 deletions

View File

@@ -42,6 +42,7 @@ RUN dnf -y install \
iso-codes \
itstool \
json-glib-devel \
lcms2-devel \
lcov \
libasan \
libattr-devel \
@@ -71,6 +72,7 @@ RUN dnf -y install \
mesa-libEGL-devel \
mesa-libGLES-devel \
meson \
mutter \
ninja-build \
pango-devel \
pcre-devel \

View File

@@ -33,6 +33,10 @@ pacman --noconfirm -S --needed \
mingw-w64-$MSYS2_ARCH-gst-plugins-bad-libs \
mingw-w64-$MSYS2_ARCH-shared-mime-info \
mingw-w64-$MSYS2_ARCH-python-gobject
mingw-w64-$MSYS2_ARCH-libpng \
mingw-w64-$MSYS2_ARCH-libjpeg-turbo \
mingw-w64-$MSYS2_ARCH-libtiff \
mingw-w64-$MSYS2_ARCH-lcms2
mkdir -p _ccache
export CCACHE_BASEDIR="$(pwd)"

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

View File

@@ -0,0 +1,86 @@
/* Color Spaces
*
* Demonstrates support for color spaces.
*
* The test images used here are taken from http://displaycal.net/icc-color-management-test/
* and are licensed under the Creative Commons BY-SA 4.0 International License
*/
#include <gtk/gtk.h>
static GtkWidget *jpeg;
static GtkWidget *png;
static GtkWidget *tiff;
static GtkWidget *noprofile;
static GtkWidget *test1;
static GtkWidget *test2;
static void
on_changed (GtkCheckButton *button,
gpointer user_data)
{
GdkTexture *texture;
const char *extension = NULL;
char *path;
if (!gtk_check_button_get_active (GTK_CHECK_BUTTON (button)))
return;
if (gtk_check_button_get_active (GTK_CHECK_BUTTON (jpeg)))
extension = ".jpg";
else if (gtk_check_button_get_active (GTK_CHECK_BUTTON (png)))
extension = ".png";
else if (gtk_check_button_get_active (GTK_CHECK_BUTTON (tiff)))
extension = ".tif";
else if (gtk_check_button_get_active (GTK_CHECK_BUTTON (noprofile)))
extension = "-expected-result-no-cm.png";
path = g_strconcat ("/colorspaces/sRGB_Gray", extension, NULL);
texture = gdk_texture_new_from_resource (path);
gtk_picture_set_paintable (GTK_PICTURE (test1), GDK_PAINTABLE (texture));
g_object_unref (texture);
path = g_strconcat ("/colorspaces/ICC-Rendering-Intent-Test", extension, NULL);
texture = gdk_texture_new_from_resource (path);
gtk_picture_set_paintable (GTK_PICTURE (test2), GDK_PAINTABLE (texture));
g_object_unref (texture);
}
GtkWidget*
do_colorspaces (GtkWidget *do_widget)
{
static GtkWidget *window;
if (!window)
{
GtkBuilder *builder;
GtkBuilderScope *scope;
scope = gtk_builder_cscope_new ();
gtk_builder_cscope_add_callback_symbol (GTK_BUILDER_CSCOPE (scope),
"on_changed", G_CALLBACK (on_changed));
builder = gtk_builder_new ();
gtk_builder_set_scope (builder, scope);
gtk_builder_add_from_resource (builder, "/colorspaces/colorspaces.ui", NULL);
window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
jpeg = GTK_WIDGET (gtk_builder_get_object (builder, "jpeg"));
png = GTK_WIDGET (gtk_builder_get_object (builder, "png"));
tiff = GTK_WIDGET (gtk_builder_get_object (builder, "tiff"));
noprofile = GTK_WIDGET (gtk_builder_get_object (builder, "noprofile"));
test1 = GTK_WIDGET (gtk_builder_get_object (builder, "test1"));
test2 = GTK_WIDGET (gtk_builder_get_object (builder, "test2"));
gtk_window_set_display (GTK_WINDOW (window),
gtk_widget_get_display (do_widget));
g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
g_object_unref (builder);
g_object_unref (scope);
}
if (!gtk_widget_get_visible (window))
gtk_widget_show (window);
else
gtk_window_destroy (GTK_WINDOW (window));
return window;
}

View File

@@ -0,0 +1,94 @@
<interface>
<object class="GtkWindow" id="window">
<property name="default-width">660</property>
<property name="default-height">660</property>
<property name="resizable">false</property>
<property name="title">Color Profiles</property>
<child>
<object class="GtkScrolledWindow">
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<child>
<object class="GtkBox">
<property name="spacing">10</property>
<child>
<object class="GtkLabel">
<property name="label">File format:</property>
</object>
</child>
<child>
<object class="GtkCheckButton" id="jpeg">
<property name="label">JPEG</property>
<property name="active">1</property>
<signal name="notify::active" handler="on_changed"/>
</object>
</child>
<child>
<object class="GtkCheckButton" id="png">
<property name="label">PNG</property>
<property name="group">jpeg</property>
<signal name="notify::active" handler="on_changed"/>
</object>
</child>
<child>
<object class="GtkCheckButton" id="tiff">
<property name="label">TIFF</property>
<property name="group">png</property>
<signal name="notify::active" handler="on_changed"/>
</object>
</child>
<child>
<object class="GtkCheckButton" id="noprofile">
<property name="label">No profile</property>
<property name="group">tiff</property>
<signal name="notify::active" handler="on_changed"/>
</object>
</child>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Test 1: Matrix-based profile</property>
<style>
<class name="title-3"/>
</style>
</object>
</child>
<child>
<object class="GtkPicture" id="test1">
<property name="hexpand">1</property>
<property name="vexpand">1</property>
<property name="can-shrink">1</property>
<property name="keep-aspect-ratio">1</property>
<property name="file">resource:///colorprofiles/sRGB_Gray.jpg</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">Test 2: Lookup table-based profile</property>
<style>
<class name="title-3"/>
</style>
</object>
</child>
<child>
<object class="GtkPicture" id="test2">
<property name="hexpand">1</property>
<property name="vexpand">1</property>
<property name="can-shrink">1</property>
<property name="keep-aspect-ratio">1</property>
<property name="file">resource:///colorprofiles/ICC-Rendering-Intent-Test.jpg</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</interface>

View File

@@ -19,6 +19,17 @@
<file>demoimage.c</file>
<file>demoimage.h</file>
</gresource>
<gresource prefix="/colorspaces">
<file>colorspaces.ui</file>
<file>sRGB_Gray.jpg</file>
<file>sRGB_Gray.png</file>
<file>sRGB_Gray.tif</file>
<file>sRGB_Gray-expected-result-no-cm.png</file>
<file>ICC-Rendering-Intent-Test.png</file>
<file>ICC-Rendering-Intent-Test.jpg</file>
<file>ICC-Rendering-Intent-Test.tif</file>
<file>ICC-Rendering-Intent-Test-expected-result-no-cm.png</file>
</gresource>
<gresource prefix="/constraints_builder">
<file>constraints_builder.ui</file>
</gresource>
@@ -255,6 +266,7 @@
<file>assistant.c</file>
<file>builder.c</file>
<file>clipboard.c</file>
<file>colorspaces.c</file>
<file>combobox.c</file>
<file>constraints.c</file>
<file>constraints_interactive.c</file>

View File

@@ -5,6 +5,7 @@ demos = files([
'assistant.c',
'builder.c',
'clipboard.c',
'colorspaces.c',
'combobox.c',
'constraints.c',
'constraints_interactive.c',

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,256 @@
#include "heif-loader.h"
#include <gtk/gtk.h>
#include <libheif/heif.h>
static void
describe_nclx_color_profile (const struct heif_color_profile_nclx *nclx,
GString *s)
{
if (nclx->color_primaries == heif_color_primaries_unspecified)
return;
if (nclx->color_primaries == heif_color_primaries_ITU_R_BT_709_5)
{
if (nclx->transfer_characteristics == heif_transfer_characteristic_IEC_61966_2_1 &&
(nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G ||
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_601_6))
{
g_string_append (s, "sRGB");
return;
}
if (nclx->transfer_characteristics == heif_transfer_characteristic_linear &&
(nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G ||
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_601_6))
{
g_string_append (s, "sRGB linear");
return;
}
}
if (nclx->color_primaries == heif_color_primaries_ITU_R_BT_2020_2_and_2100_0)
{
if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_PQ &&
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance)
{
g_string_append (s, "BT.2020 PQ");
return;
}
if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_HLG &&
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance)
{
g_string_append (s, "BT.2020 HLG");
return;
}
}
if (nclx->color_primaries == heif_color_primaries_SMPTE_EG_432_1)
{
if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_PQ)
{
g_string_append (s, "P3 PQ");
return;
}
}
g_string_append_printf (s, "%d/%d/%d",
nclx->color_primaries,
nclx->matrix_coefficients,
nclx->transfer_characteristics);
}
GdkTexture *
load_heif_image (const char *resource_path,
GString *details,
GError **error)
{
struct heif_context *ctx;
struct heif_error err;
struct heif_image_handle *handle = NULL;
struct heif_image *image = NULL;
enum heif_chroma chroma;
GdkMemoryFormat format;
const char *format_name;
uint8_t *data = NULL;
gsize size;
int width, height, bpp, stride, bits;
GBytes *bytes;
GdkTexture *texture = NULL;
GdkColorSpace *color_space = NULL;
char *profile_type = NULL;
ctx = heif_context_alloc ();
bytes = g_resources_lookup_data (resource_path, 0, NULL);
data = (uint8_t *) g_bytes_get_data (bytes, &size);
err = heif_context_read_from_memory (ctx, data, size, NULL);
g_bytes_unref (bytes);
if (err.code != heif_error_Ok)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
goto out;
}
err = heif_context_get_primary_image_handle (ctx, &handle);
if (err.code != heif_error_Ok)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
goto out;
}
switch (heif_image_handle_get_color_profile_type (handle))
{
case heif_color_profile_type_not_present:
break;
case heif_color_profile_type_rICC:
case heif_color_profile_type_prof:
{
guchar *icc_data;
gsize icc_size;
profile_type = g_strdup ("icc");
icc_size = heif_image_handle_get_raw_color_profile_size (handle);
icc_data = g_new0 (guchar, icc_size);
err = heif_image_handle_get_raw_color_profile (handle, icc_data);
if (err.code == heif_error_Ok)
{
bytes = g_bytes_new (icc_data, icc_size);
color_space = gdk_color_space_new_from_icc_profile (bytes, NULL);
g_bytes_unref (bytes);
}
g_free (icc_data);
}
break;
case heif_color_profile_type_nclx:
{
struct heif_color_profile_nclx *nclx = NULL;
err = heif_image_handle_get_nclx_color_profile (handle, &nclx);
if (err.code == heif_error_Ok)
{
GString *s;
s = g_string_new ("");
describe_nclx_color_profile (nclx, s);
profile_type = g_string_free (s, FALSE);
color_space = gdk_color_space_new_from_cicp (nclx->color_primaries,
nclx->transfer_characteristics,
0,
TRUE);
heif_nclx_color_profile_free (nclx);
}
}
break;
default:
g_assert_not_reached ();
}
g_string_append_printf (details, "%d×%d pixels\n%d bits of luma, %d bits of chroma%s\n",
heif_image_handle_get_width (handle),
heif_image_handle_get_height (handle),
heif_image_handle_get_luma_bits_per_pixel (handle),
heif_image_handle_get_chroma_bits_per_pixel (handle),
heif_image_handle_has_alpha_channel (handle) ? ", with alpha" : "");
if (profile_type)
{
g_string_append_printf (details, "color profile: %s\n", profile_type);
g_free (profile_type);
}
if (color_space == NULL)
color_space = g_object_ref (gdk_color_space_get_srgb ());
if (heif_image_handle_has_alpha_channel (handle))
{
if (heif_image_handle_get_luma_bits_per_pixel (handle) > 8 ||
heif_image_handle_get_chroma_bits_per_pixel (handle) > 8)
{
chroma = heif_chroma_interleaved_RRGGBBAA_BE;
format = GDK_MEMORY_R16G16B16A16;
format_name = "R<sub>16</sub>G<sub>16</sub>B<sub>16</sub>A<sub>16</sub>";
}
else
{
chroma = heif_chroma_interleaved_RGBA;
format = GDK_MEMORY_R8G8B8A8;
format_name = "R<sub>8</sub>G<sub>8</sub>B<sub>8</sub>A<sub>8</sub>";
}
}
else
{
if (heif_image_handle_get_luma_bits_per_pixel (handle) > 8 ||
heif_image_handle_get_chroma_bits_per_pixel (handle) > 8)
{
chroma = heif_chroma_interleaved_RRGGBB_BE;
format = GDK_MEMORY_R16G16B16;
format_name = "R<sub>16</sub>G<sub>16</sub>B<sub>16</sub>";
}
else
{
chroma = heif_chroma_interleaved_RGB;
format = GDK_MEMORY_R8G8B8;
format_name = "R<sub>8</sub>G<sub>8</sub>B<sub>8</sub>";
}
}
err = heif_decode_image (handle, &image, heif_colorspace_RGB, chroma, NULL);
if (err.code != heif_error_Ok)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
goto out;
}
width = heif_image_get_width (image, heif_channel_interleaved);
height = heif_image_get_height (image, heif_channel_interleaved);
bpp = heif_image_get_bits_per_pixel (image, heif_channel_interleaved);
data = (uint8_t*)heif_image_get_plane_readonly (image, heif_channel_interleaved, &stride);
bits = heif_image_get_bits_per_pixel_range (image, heif_channel_interleaved);
g_string_append_printf (details, "texture format %s", format_name);
if (format == GDK_MEMORY_R16G16B16A16 || format == GDK_MEMORY_R16G16B16)
{
/* Shift image data to full 16bit range */
int shift = 16 - bits;
if (shift > 0)
{
for (int y = 0; y < height; ++y)
{
uint8_t *row = data + y * stride;
for (int x = 0; x < stride; x += 2)
{
uint8_t* p = &row[x];
int v = (p[0] << 8) | p[1];
v = (v << shift) | (v >> (16 - shift));
p[1] = (uint8_t) (v >> 8);
p[0] = (uint8_t) (v & 0xFF);
}
}
}
}
bytes = g_bytes_new_with_free_func (data, height * stride * bpp, (GDestroyNotify)heif_image_release, image);
image = NULL;
texture = gdk_memory_texture_new_with_color_space (width, height,
format, color_space,
bytes, stride);
g_bytes_unref (bytes);
g_object_unref (color_space);
out:
heif_context_free (ctx);
if (handle)
heif_image_handle_release (handle);
if (image)
heif_image_release (image);
return texture;
}

View File

@@ -0,0 +1,7 @@
#pragma once
#include <gtk/gtk.h>
GdkTexture *load_heif_image (const char *resource_path,
GString *details,
GError **error);

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

View File

@@ -73,10 +73,18 @@ else
)
endif
widgetfactory_cflags = []
widgetfactory_sources = [ 'widget-factory.c' ]
if libheif_dep.found()
widgetfactory_cflags += '-DHAVE_LIBHEIF'
widgetfactory_sources += 'heif-loader.c'
endif
executable('gtk4-widget-factory',
sources: ['widget-factory.c', widgetfactory_resources],
c_args: common_cflags,
dependencies: [ libgtk_dep, demo_conf_h ],
sources: [ widgetfactory_sources, widgetfactory_resources],
c_args: [ common_cflags, widgetfactory_cflags ],
dependencies: [ libgtk_dep, libheif_dep, demo_conf_h ],
include_directories: confinc,
win_subsystem: 'windows',
link_args: extra_demo_ldflags,

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

View File

@@ -27,6 +27,8 @@
#include "demo_conf.h"
#include "heif-loader.h"
static void
change_dark_state (GSimpleAction *action,
GVariant *state,
@@ -2383,6 +2385,20 @@ activate (GApplication *app)
model = (GMenuModel *)gtk_builder_get_object (builder, "new_style_context_menu_model");
set_up_context_popover (widget, model);
widget = (GtkWidget *)gtk_builder_get_object (builder, "hdr_picture");
#ifdef HAVE_LIBHEIF
widget2 = (GtkWidget *)gtk_builder_get_object (builder, "hdr_label");
GString *details = g_string_new ("");
GdkTexture *texture = load_heif_image ("/org/gtk/WidgetFactory4/hdr-example.heif", details, NULL);
gtk_picture_set_paintable (GTK_PICTURE (widget), GDK_PAINTABLE (texture));
gtk_label_set_label (GTK_LABEL (widget2), details->str);
g_string_free (details, TRUE);
g_object_unref (texture);
#else
widget2 = gtk_widget_get_ancestor (widget, GTK_TYPE_NOTEBOOK);
gtk_notebook_remove_page (GTK_NOTEBOOK (widget2), 2);
#endif
gtk_window_present (window);
g_object_unref (builder);

View File

@@ -6,3 +6,10 @@
.toolbar {
-gtk-icon-style: symbolic;
}
.gtk-gradient-color {
background: linear-gradient(to right, lime, red);
}
.gtk-gradient-monochrome {
background: linear-gradient(to right, black, white);
}

View File

@@ -110,6 +110,7 @@
<file>icons/scalable/status/weather-severe-alert-symbolic.svg</file>
<file>icons/scalable/status/weather-showers-symbolic.svg</file>
<file>icons/scalable/status/weather-snow-symbolic.svg</file>
<file>hdr-example.heif</file>
<file alias='icons/scalable/apps/org.gtk.WidgetFactory4.svg'>data/scalable/apps/org.gtk.WidgetFactory4.svg</file>
@@ -120,5 +121,12 @@
<file>portland-rose.jpg</file>
<file>nyc.jpg</file>
<file>beach.jpg</file>
<file>linear-gradient-color.png</file>
<file>linear-gradient-monochrome.png</file>
<file>srgb-gradient-color.png</file>
<file>srgb-gradient-monochrome.png</file>
<file>color-profile-check.png</file>
<file>color-profile-check.jpeg</file>
<file>color-profile-check.tiff</file>
</gresource>
</gresources>

View File

@@ -1347,13 +1347,173 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
<child>
<object class="GtkNotebookPage">
<property name="child">
<object class="GtkBox" id="box8">
<property name="orientation">1</property>
<object class="GtkGrid">
<property name="hexpand">0</property>
<property name="row-spacing">6</property>
<property name="column-spacing">6</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<property name="margin-top">6</property>
<property name="margin-bottom">6</property>
<property name="row-homogeneous">1</property>
<property name="valign">start</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">color</property>
<style>
<class name="caption-heading"/>
</style>
<layout>
<property name="column">0</property>
<property name="row">0</property>
<property name="column-span">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">sRGB</property>
<layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkPicture">
<property name="file">resource:///org/gtk/WidgetFactory4/srgb-gradient-color.png</property>
<property name="can-shrink">0</property>
<property name="keep-aspect-ratio">0</property>
<property name="hexpand">1</property>
<layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">GTK</property>
<layout>
<property name="column">0</property>
<property name="row">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkPicture">
<property name="width-request">128</property>
<property name="keep-aspect-ratio">0</property>
<style>
<class name="gtk-gradient-color"/>
</style>
<layout>
<property name="column">1</property>
<property name="row">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">linear</property>
<layout>
<property name="column">0</property>
<property name="row">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkPicture">
<property name="file">resource:///org/gtk/WidgetFactory4/linear-gradient-color.png</property>
<property name="can-shrink">0</property>
<property name="keep-aspect-ratio">0</property>
<layout>
<property name="column">1</property>
<property name="row">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">monochrome</property>
<style>
<class name="caption-heading"/>
</style>
<layout>
<property name="column">0</property>
<property name="row">4</property>
<property name="column-span">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">sRGB</property>
<layout>
<property name="column">0</property>
<property name="row">5</property>
</layout>
</object>
</child>
<child>
<object class="GtkPicture">
<property name="file">resource:///org/gtk/WidgetFactory4/srgb-gradient-monochrome.png</property>
<property name="can-shrink">0</property>
<property name="keep-aspect-ratio">0</property>
<property name="hexpand">1</property>
<layout>
<property name="column">1</property>
<property name="row">5</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">GTK</property>
<layout>
<property name="column">0</property>
<property name="row">6</property>
</layout>
</object>
</child>
<child>
<object class="GtkPicture">
<property name="width-request">128</property>
<property name="keep-aspect-ratio">0</property>
<style>
<class name="gtk-gradient-monochrome"/>
</style>
<layout>
<property name="column">1</property>
<property name="row">6</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">linear</property>
<layout>
<property name="column">0</property>
<property name="row">7</property>
</layout>
</object>
</child>
<child>
<object class="GtkPicture">
<property name="file">resource:///org/gtk/WidgetFactory4/linear-gradient-monochrome.png</property>
<property name="can-shrink">0</property>
<property name="keep-aspect-ratio">0</property>
<layout>
<property name="column">1</property>
<property name="row">7</property>
</layout>
</object>
</child>
</object>
</property>
<property name="tab">
<object class="GtkLabel" id="label8">
<property name="label" translatable="1">page 1</property>
<property name="label" translatable="1">Gradients</property>
</object>
</property>
</object>
@@ -1362,13 +1522,92 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
<object class="GtkNotebookPage">
<property name="position">1</property>
<property name="child">
<object class="GtkBox" id="box10">
<property name="orientation">1</property>
<object class="GtkGrid">
<property name="hexpand">0</property>
<property name="row-spacing">6</property>
<property name="column-spacing">6</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<property name="margin-top">6</property>
<property name="margin-bottom">6</property>
<property name="row-homogeneous">1</property>
<property name="valign">start</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">image loading</property>
<style>
<class name="caption-heading"/>
</style>
<layout>
<property name="column">0</property>
<property name="row">0</property>
<property name="column-span">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkImage">
<property name="paintable">resource:///org/gtk/WidgetFactory4/color-profile-check.png</property>
<layout>
<property name="column">0</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">PNG</property>
<property name="hexpand">1</property>
<layout>
<property name="column">1</property>
<property name="row">1</property>
</layout>
</object>
</child>
<child>
<object class="GtkImage">
<property name="paintable">resource:///org/gtk/WidgetFactory4/color-profile-check.jpeg</property>
<layout>
<property name="column">0</property>
<property name="row">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">JPEG</property>
<property name="hexpand">1</property>
<layout>
<property name="column">1</property>
<property name="row">2</property>
</layout>
</object>
</child>
<child>
<object class="GtkImage">
<property name="paintable">resource:///org/gtk/WidgetFactory4/color-profile-check.tiff</property>
<layout>
<property name="column">0</property>
<property name="row">3</property>
</layout>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="label">TIFF</property>
<property name="hexpand">1</property>
<layout>
<property name="column">1</property>
<property name="row">3</property>
</layout>
</object>
</child>
</object>
</property>
<property name="tab">
<object class="GtkLabel" id="label9">
<property name="label" translatable="1">page 2</property>
<property name="label" translatable="1">Images</property>
</object>
</property>
</object>
@@ -1378,12 +1617,26 @@ Suspendisse feugiat quam quis dolor accumsan cursus.</property>
<property name="position">2</property>
<property name="child">
<object class="GtkBox" id="box11">
<property name="orientation">1</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkPicture" id="hdr_picture">
<property name="can-shrink">1</property>
</object>
</child>
<child>
<object class="GtkLabel" id="hdr_label">
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
<property name="use-markup">1</property>
</object>
</child>
</object>
</property>
<property name="tab">
<object class="GtkLabel" id="label10">
<property name="label" translatable="1">page 3</property>
<property name="label" translatable="1">HDR</property>
</object>
</property>
</object>

View File

@@ -39,26 +39,19 @@ gdk_broadway_cairo_context_begin_frame (GdkDrawContext *draw_context,
{
GdkBroadwayCairoContext *self = GDK_BROADWAY_CAIRO_CONTEXT (draw_context);
GdkSurface *surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self));
cairo_t *cr;
cairo_region_t *repaint_region;
int width, height, scale;
int width, height;
width = gdk_surface_get_width (surface);
height = gdk_surface_get_height (surface);
scale = gdk_surface_get_scale_factor (surface);
self->paint_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
width * scale, height * scale);
cairo_surface_set_device_scale (self->paint_surface, scale, scale);
self->paint_surface = gdk_surface_create_similar_surface (surface,
CAIRO_CONTENT_COLOR_ALPHA,
width,
height);
repaint_region = cairo_region_create_rectangle (&(cairo_rectangle_int_t) { 0, 0, width, height });
cairo_region_union (region, repaint_region);
cairo_region_destroy (repaint_region);
/* clear the repaint area */
cr = cairo_create (self->paint_surface);
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_fill (cr);
cairo_destroy (cr);
}
static void

View File

@@ -19,7 +19,7 @@
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include "config.h"
@@ -130,6 +130,7 @@ static const GdkDebugKey gdk_debug_keys[] = {
{ "vulkan-validate", GDK_DEBUG_VULKAN_VALIDATE, "Load the Vulkan validation layer", TRUE },
{ "default-settings",GDK_DEBUG_DEFAULT_SETTINGS, "Force default values for xsettings", TRUE },
{ "high-depth", GDK_DEBUG_HIGH_DEPTH, "Use high bit depth rendering if possible", TRUE },
{ "srgb", GDK_DEBUG_SRGB, "Force sRRGB rendering and turn off color profiles", TRUE },
};

View File

@@ -31,6 +31,7 @@
#include <gdk/gdkcairo.h>
#include <gdk/gdkcairocontext.h>
#include <gdk/gdkclipboard.h>
#include <gdk/gdkcolorspace.h>
#include <gdk/gdkconfig.h>
#include <gdk/gdkcontentdeserializer.h>
#include <gdk/gdkcontentformats.h>

View File

@@ -1,5 +1,5 @@
/* GDK - The GIMP Drawing Kit
* Copyright (C) 2005 Red Hat, Inc.
* Copyright (C) 2005 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
@@ -19,6 +19,10 @@
#include "gdkcairoprivate.h"
#include "gdkcolorspace.h"
#include "gdkcolorprivate.h"
#include "gdkmemoryformatprivate.h"
#include <math.h>
/**
@@ -32,14 +36,20 @@ void
gdk_cairo_set_source_rgba (cairo_t *cr,
const GdkRGBA *rgba)
{
GdkColor color;
const float *components;
g_return_if_fail (cr != NULL);
g_return_if_fail (rgba != NULL);
gdk_color_convert_rgba (&color, gdk_cairo_get_color_space (cr), rgba);
components = gdk_color_get_components (&color);
cairo_set_source_rgba (cr,
rgba->red,
rgba->green,
rgba->blue,
rgba->alpha);
components[0],
components[1],
components[2],
gdk_color_get_alpha (&color));
gdk_color_finish (&color);
}
/**
@@ -335,3 +345,97 @@ gdk_cairo_region_from_clip (cairo_t *cr)
return region;
}
static cairo_user_data_key_t color_space_key;
/**
* gdk_cairo_surface_set_color_space:
* @surface: a surface
* @color_space: the color space to attach to the surface
*
* Attaches a `GdkColorSpace` to the Cairo surface.
*
* This is just auxiliary data for use by GTK, no Cairo functions
* do interact with this information.
*
* Note that all Cairo compositing operations are assumed to happen
* in a linear RGB color space, so if you want to use the surface
* as a target for rendering in a color managed way, you should use
* such a color space.
*
* The default color space is assumed to be sRGB, which is not
* linear.
*
* Since: 4.10
*/
void
gdk_cairo_surface_set_color_space (cairo_surface_t *surface,
GdkColorSpace *color_space)
{
g_return_if_fail (surface != NULL);
g_return_if_fail (GDK_IS_COLOR_SPACE (color_space));
cairo_surface_set_user_data (surface,
&color_space_key,
g_object_ref (color_space),
g_object_unref);
}
/**
* gdk_cairo_surface_get_color_space:
* @surface: a surface
*
* Gets the color space GTK assumes for the surface. See
* [method@Gdk.CairoSurface.set_color_space] for details.
*
* Returns: (transfer none): the color space
*
* Since: 4.10
*/
GdkColorSpace *
gdk_cairo_surface_get_color_space (cairo_surface_t *surface)
{
GdkColorSpace *color_space;
g_return_val_if_fail (surface != NULL, gdk_color_space_get_srgb ());
color_space = cairo_surface_get_user_data (surface, &color_space_key);
if (color_space == NULL)
color_space = gdk_color_space_get_srgb ();
return color_space;
}
/**
* gdk_cairo_get_color_space:
* @cr: a cairo context
*
* Gets the color space GTK assumes for the cairo context.
*
* Returns: (transfer none): the color space
*
* Since: 4.10
*/
GdkColorSpace *
gdk_cairo_get_color_space (cairo_t *cr)
{
GdkColorSpace *color_space;
cairo_surface_t *surface;
g_return_val_if_fail (cr != NULL, gdk_color_space_get_srgb ());
surface = cairo_get_group_target (cr);
color_space = cairo_surface_get_user_data (surface, &color_space_key);
if (color_space != NULL)
return color_space;
/* theoretically, we should walk the whole group stack, but I don't
* think Cairo lets us do that
*/
surface = cairo_get_target (cr);
color_space = cairo_surface_get_user_data (surface, &color_space_key);
if (color_space != NULL)
return color_space;
return gdk_color_space_get_srgb ();
}

View File

@@ -1,5 +1,5 @@
/* GDK - The GIMP Drawing Kit
* Copyright (C) 2005 Red Hat, Inc.
* Copyright (C) 2005 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
@@ -46,9 +46,15 @@ void gdk_cairo_region (cairo_t *cr,
const cairo_region_t *region);
GDK_AVAILABLE_IN_ALL
cairo_region_t *
gdk_cairo_region_create_from_surface
(cairo_surface_t *surface);
cairo_region_t * gdk_cairo_region_create_from_surface (cairo_surface_t *surface);
GDK_AVAILABLE_IN_4_10
void gdk_cairo_surface_set_color_space (cairo_surface_t *surface,
GdkColorSpace *color_space);
GDK_AVAILABLE_IN_4_10
GdkColorSpace * gdk_cairo_surface_get_color_space (cairo_surface_t *surface);
GDK_AVAILABLE_IN_4_10
GdkColorSpace * gdk_cairo_get_color_space (cairo_t *cr);
GDK_DEPRECATED_IN_4_6_FOR(gdk_gl_texture_new)
void gdk_cairo_draw_from_gl (cairo_t *cr,

273
gdk/gdkcicpcolorspace.c Normal file
View File

@@ -0,0 +1,273 @@
/* gdkcicpcolorspace.c
*
* Copyright 2022 (c) 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/>.
*/
#include "config.h"
#include "gdkcicpcolorspaceprivate.h"
#include "gdklcmscolorspaceprivate.h"
#include <glib/gi18n-lib.h>
struct _GdkCicpColorSpace
{
GdkColorSpace parent_instance;
int color_primaries;
int transfer_characteristics;
int matrix_coefficients;
gboolean full_range;
GdkColorSpace *lcms;
};
struct _GdkCicpColorSpaceClass
{
GdkColorSpaceClass parent_class;
};
G_DEFINE_TYPE (GdkCicpColorSpace, gdk_cicp_color_space, GDK_TYPE_COLOR_SPACE)
static gboolean
gdk_cicp_color_space_supports_format (GdkColorSpace *space,
GdkMemoryFormat format)
{
GdkCicpColorSpace *self = GDK_CICP_COLOR_SPACE (space);
if (self->lcms)
return gdk_color_space_supports_format (self->lcms, format);
return FALSE;
}
static GBytes *
gdk_cicp_color_space_save_to_icc_profile (GdkColorSpace *space,
GError **error)
{
GdkCicpColorSpace *self = GDK_CICP_COLOR_SPACE (space);
if (self->lcms)
return gdk_color_space_save_to_icc_profile (self->lcms, error);
return NULL;
}
static int
gdk_cicp_color_space_get_n_components (GdkColorSpace *space)
{
GdkCicpColorSpace *self = GDK_CICP_COLOR_SPACE (space);
if (self->lcms)
return gdk_color_space_get_n_components (self->lcms);
return 0;
}
static void
gdk_cicp_color_space_dispose (GObject *object)
{
GdkCicpColorSpace *self = GDK_CICP_COLOR_SPACE (object);
g_clear_object (&self->lcms);
G_OBJECT_CLASS (gdk_cicp_color_space_parent_class)->dispose (object);
}
static void
gdk_cicp_color_space_class_init (GdkCicpColorSpaceClass *klass)
{
GdkColorSpaceClass *color_space_class = GDK_COLOR_SPACE_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
color_space_class->supports_format = gdk_cicp_color_space_supports_format;
color_space_class->save_to_icc_profile = gdk_cicp_color_space_save_to_icc_profile;
color_space_class->get_n_components = gdk_cicp_color_space_get_n_components;
gobject_class->dispose = gdk_cicp_color_space_dispose;
}
static void
gdk_cicp_color_space_init (GdkCicpColorSpace *self)
{
}
static GdkColorSpace *
lcms_from_cicp (int color_primaries,
int transfer_characteristics,
int matrix_coefficients,
gboolean full_range)
{
cmsHPROFILE profile = NULL;
cmsCIExyY whitepoint;
cmsCIExyYTRIPLE primaries;
cmsToneCurve *curve[3];
cmsCIExyY whiteD65 = (cmsCIExyY) { 0.3127, 0.3290, 1.0 };
cmsCIExyY whiteC = (cmsCIExyY) { 0.310, 0.316, 1.0 };
cmsFloat64Number srgb_parameters[5] =
{ 2.4, 1.0 / 1.055, 0.055 / 1.055, 1.0 / 12.92, 0.04045 };
cmsFloat64Number rec709_parameters[5] =
{ 2.2, 1.0 / 1.099, 0.099 / 1.099, 1.0 / 4.5, 0.081 };
/* We only support full-range RGB profiles */
g_assert (matrix_coefficients == 0);
g_assert (full_range);
if (color_primaries == 0 /* ITU_R_BT_709_5 */)
{
if (transfer_characteristics == 13 /* IEC_61966_2_1 */)
return g_object_ref (gdk_color_space_get_srgb ());
else if (transfer_characteristics == 8 /* linear */)
return g_object_ref (gdk_color_space_get_srgb_linear ());
}
switch (color_primaries)
{
case 1:
primaries.Green = (cmsCIExyY) { 0.300, 0.600, 1.0 };
primaries.Blue = (cmsCIExyY) { 0.150, 0.060, 1.0 };
primaries.Red = (cmsCIExyY) { 0.640, 0.330, 1.0 };
whitepoint = whiteD65;
break;
case 4:
primaries.Green = (cmsCIExyY) { 0.21, 0.71, 1.0 };
primaries.Blue = (cmsCIExyY) { 0.14, 0.08, 1.0 };
primaries.Red = (cmsCIExyY) { 0.67, 0.33, 1.0 };
whitepoint = whiteC;
break;
case 5:
primaries.Green = (cmsCIExyY) { 0.29, 0.60, 1.0 };
primaries.Blue = (cmsCIExyY) { 0.15, 0.06, 1.0 };
primaries.Red = (cmsCIExyY) { 0.64, 0.33, 1.0 };
whitepoint = whiteD65;
break;
case 6:
case 7:
primaries.Green = (cmsCIExyY) { 0.310, 0.595, 1.0 };
primaries.Blue = (cmsCIExyY) { 0.155, 0.070, 1.0 };
primaries.Red = (cmsCIExyY) { 0.630, 0.340, 1.0 };
whitepoint = whiteD65;
break;
case 8:
primaries.Green = (cmsCIExyY) { 0.243, 0.692, 1.0 };
primaries.Blue = (cmsCIExyY) { 0.145, 0.049, 1.0 };
primaries.Red = (cmsCIExyY) { 0.681, 0.319, 1.0 };
whitepoint = whiteC;
break;
case 9:
primaries.Green = (cmsCIExyY) { 0.170, 0.797, 1.0 };
primaries.Blue = (cmsCIExyY) { 0.131, 0.046, 1.0 };
primaries.Red = (cmsCIExyY) { 0.708, 0.292, 1.0 };
whitepoint = whiteD65;
break;
case 10:
primaries.Green = (cmsCIExyY) { 0.0, 1.0, 1.0 };
primaries.Blue = (cmsCIExyY) { 0.0, 0.0, 1.0 };
primaries.Red = (cmsCIExyY) { 1.0, 0.0, 1.0 };
whitepoint = (cmsCIExyY) { 0.333333, 0.333333, 1.0 };
break;
case 11:
primaries.Green = (cmsCIExyY) { 0.265, 0.690, 1.0 };
primaries.Blue = (cmsCIExyY) { 0.150, 0.060, 1.0 };
primaries.Red = (cmsCIExyY) { 0.680, 0.320, 1.0 };
whitepoint = (cmsCIExyY) { 0.314, 0.351, 1.0 };
break;
case 12:
primaries.Green = (cmsCIExyY) { 0.265, 0.690, 1.0 };
primaries.Blue = (cmsCIExyY) { 0.150, 0.060, 1.0 };
primaries.Red = (cmsCIExyY) { 0.680, 0.320, 1.0 };
whitepoint = whiteD65;
break;
case 22:
primaries.Green = (cmsCIExyY) { 0.295, 0.605, 1.0 };
primaries.Blue = (cmsCIExyY) { 0.155, 0.077, 1.0 };
primaries.Red = (cmsCIExyY) { 0.630, 0.340, 1.0 };
whitepoint = whiteD65;
break;
default:
return NULL;
}
switch (transfer_characteristics)
{
case 1: /* ITU_R_BT_709_5 */
curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve (NULL, 4,
rec709_parameters);
profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
cmsFreeToneCurve (curve[0]);
break;
case 4: /* ITU_R_BT_470_6_System_M */
curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 2.2f);
profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
cmsFreeToneCurve (curve[0]);
break;
case 5: /* ITU_R_BT_470_6_System_B_G */
curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 2.8f);
profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
cmsFreeToneCurve (curve[0]);
break;
case 8: /* linear */
curve[0] = curve[1] = curve[2] = cmsBuildGamma (NULL, 1.0f);
profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
cmsFreeToneCurve (curve[0]);
break;
/* FIXME
* We need to handle at least 16 (PQ) here.
* Problem is: lcms can't do it
*/
case 13: /* IEC_61966_2_1 */
default:
curve[0] = curve[1] = curve[2] = cmsBuildParametricToneCurve (NULL, 4,
srgb_parameters);
profile = cmsCreateRGBProfile (&whitepoint, &primaries, curve);
cmsFreeToneCurve (curve[0]);
break;
}
if (profile)
return gdk_lcms_color_space_new_from_lcms_profile (profile);
return NULL;
}
GdkColorSpace *
gdk_color_space_new_from_cicp (int color_primaries,
int transfer_characteristics,
int matrix_coefficients,
gboolean full_range)
{
GdkCicpColorSpace *result;
result = g_object_new (GDK_TYPE_CICP_COLOR_SPACE, NULL);
result->color_primaries = color_primaries;
result->transfer_characteristics = transfer_characteristics;
result->matrix_coefficients = matrix_coefficients;
result->full_range = full_range;
result->lcms = lcms_from_cicp (color_primaries,
transfer_characteristics,
matrix_coefficients,
full_range);
return GDK_COLOR_SPACE (result);
}
GdkColorSpace *
gdk_cicp_color_space_get_lcms_color_space (GdkColorSpace *space)
{
GdkCicpColorSpace *self = GDK_CICP_COLOR_SPACE (space);
return self->lcms;
}

View File

@@ -0,0 +1,45 @@
/* gdkcicpcolorspaceprivate.h
*
* Copyright 2022 (c) 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/>.
*/
#ifndef __GDK_CICP_COLOR_SPACE_PRIVATE_H__
#define __GDK_CICP_COLOR_SPACE_PRIVATE_H__
#include "gdkcolorspaceprivate.h"
G_BEGIN_DECLS
#define GDK_TYPE_CICP_COLOR_SPACE (gdk_cicp_color_space_get_type ())
#define GDK_CICP_COLOR_SPACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_CICP_COLOR_SPACE, GdkCicpColorSpace))
#define GDK_IS_CICP_COLOR_SPACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_CICP_COLOR_SPACE))
#define GDK_CICP_COLOR_SPACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_CICP_COLOR_SPACE, GdkCicpColorSpaceClass))
#define GDK_IS_CICP_COLOR_SPACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_CICP_COLOR_SPACE))
#define GDK_CICP_COLOR_SPACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_CICP_COLOR_SPACE, GdkCicpColorSpaceClass))
typedef struct _GdkCicpColorSpace GdkCicpColorSpace;
typedef struct _GdkCicpColorSpaceClass GdkCicpColorSpaceClass;
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkCicpColorSpace, g_object_unref)
GType gdk_cicp_color_space_get_type (void) G_GNUC_CONST;
GdkColorSpace * gdk_cicp_color_space_get_lcms_color_space (GdkColorSpace *self);
G_END_DECLS
#endif /* __GDK_CICP_COLOR_SPACE_PRIVATE_H__ */

115
gdk/gdkcolor.c Normal file
View File

@@ -0,0 +1,115 @@
/* GDK - The GIMP Drawing Kit
*
* Copyright (C) 2021 Benjamin Otte
*
* 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/>.
*/
#include "config.h"
#include "gdkcolorprivate.h"
#include "gdkcolorspaceprivate.h"
#include "gdklcmscolorspaceprivate.h"
#include <lcms2.h>
static inline cmsHTRANSFORM *
gdk_color_get_transform (GdkColorSpace *src,
GdkColorSpace *dest)
{
return gdk_color_space_lookup_transform (src, TYPE_RGBA_FLT, dest, TYPE_RGBA_FLT);
}
void
gdk_color_convert (GdkColor *self,
GdkColorSpace *color_space,
const GdkColor *other)
{
gdk_color_init (self,
color_space,
other->alpha,
NULL,
gdk_color_space_get_n_components (color_space));
cmsDoTransform (gdk_color_get_transform (other->color_space, color_space),
gdk_color_get_components (other),
(float *) gdk_color_get_components (self),
1);
}
void
gdk_color_convert_rgba (GdkColor *self,
GdkColorSpace *color_space,
const GdkRGBA *rgba)
{
gdk_color_init (self,
color_space,
rgba->alpha,
NULL,
gdk_color_space_get_n_components (color_space));
cmsDoTransform (gdk_color_get_transform (gdk_color_space_get_srgb (), color_space),
(float[3]) { rgba->red, rgba->green, rgba->blue },
(float *) gdk_color_get_components (self),
1);
}
void
gdk_color_mix (GdkColor *self,
GdkColorSpace *color_space,
const GdkColor *src1,
const GdkColor *src2,
double progress)
{
if (src1->color_space != color_space)
{
GdkColor tmp;
gdk_color_convert (&tmp, color_space, src1);
gdk_color_mix (self, color_space, &tmp, src2, progress);
}
else if (src2->color_space != color_space)
{
GdkColor tmp;
gdk_color_convert (&tmp, color_space, src2);
gdk_color_mix (self, color_space, src1, &tmp, progress);
}
else
{
gsize i, n;
const float *s1, *s2;
float *d;
n = gdk_color_space_get_n_components (color_space);
gdk_color_init (self,
color_space,
src1->alpha * (1.0 - progress) + src2->alpha * progress,
NULL, n);
d = (float *) gdk_color_get_components (self);
s1 = gdk_color_get_components (src1);
s2 = gdk_color_get_components (src2);
if (self->alpha == 0)
{
for (i = 0; i < n; i++)
d[i] = s1[i] * (1.0 - progress) + s2[i] * progress;
}
else
{
for (i = 0; i < n; i++)
d[i] = (s1[i] * src1->alpha * (1.0 - progress) + s2[i] * src2->alpha * progress) / self->alpha;
}
}
}

73
gdk/gdkcolorprivate.h Normal file
View File

@@ -0,0 +1,73 @@
/* GDK - The GIMP Drawing Kit
*
* Copyright (C) 2021 Benjamin Otte
*
* 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/>.
*/
#ifndef __GDK_COLOR_PRIVATE_H__
#define __GDK_COLOR_PRIVATE_H__
#include <gdk/gdkcolorspace.h>
#include <gdk/gdkrgba.h>
/* RGB - and it makes the struct size a multiple of 8 bytes, ie pointer-aligned */
#define GDK_COLOR_MAX_NATIVE_COMPONENTS 3
typedef struct _GdkColor GdkColor;
struct _GdkColor
{
GdkColorSpace *color_space;
float alpha;
union {
float values[GDK_COLOR_MAX_NATIVE_COMPONENTS];
float *components;
};
};
G_STATIC_ASSERT (sizeof (GdkColor) % sizeof (gpointer) == 0);
static inline void gdk_color_init (GdkColor *self,
GdkColorSpace *color_space,
float alpha,
float *components,
gsize n_components);
static inline void gdk_color_init_from_rgba (GdkColor *self,
const GdkRGBA *rgba);
static inline void gdk_color_finish (GdkColor *self);
void gdk_color_convert (GdkColor *self,
GdkColorSpace *color_space,
const GdkColor *other);
void gdk_color_convert_rgba (GdkColor *self,
GdkColorSpace *color_space,
const GdkRGBA *rgba);
void gdk_color_mix (GdkColor *self,
GdkColorSpace *color_space,
const GdkColor *src1,
const GdkColor *src2,
double progress);
static inline GdkColorSpace * gdk_color_get_color_space (const GdkColor *self);
static inline float gdk_color_get_alpha (const GdkColor *self);
static inline const float * gdk_color_get_components (const GdkColor *self);
static inline gsize gdk_color_get_n_components (const GdkColor *self);
#include "gdkcolorprivateimpl.h"
G_END_DECLS
#endif

97
gdk/gdkcolorprivateimpl.h Normal file
View File

@@ -0,0 +1,97 @@
/* GDK - The GIMP Drawing Kit
*
* Copyright (C) 2021 Benjamin Otte
*
* 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/>.
*/
static inline gboolean
gdk_color_is_allocated (const GdkColor *self)
{
return GPOINTER_TO_SIZE (self->color_space) & 1;
}
static inline void
gdk_color_init (GdkColor *self,
GdkColorSpace *color_space,
float alpha,
float *components,
gsize n_components)
{
gboolean allocated = n_components > GDK_COLOR_MAX_NATIVE_COMPONENTS;
g_assert (n_components == gdk_color_space_get_n_components (color_space));
self->color_space = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (g_object_ref (color_space)) | allocated);
self->alpha = alpha;
if (allocated)
{
if (components)
self->components = g_memdup (components, sizeof (float) * n_components);
else
self->components = g_new (float, n_components);
}
else
{
if (components)
memcpy (self->values, components, sizeof (float) * n_components);
}
}
static inline void
gdk_color_init_from_rgba (GdkColor *self,
const GdkRGBA *rgba)
{
gdk_color_init (self,
gdk_color_space_get_srgb (),
rgba->alpha,
(float[3]) { rgba->red, rgba->green, rgba->blue },
3);
}
static inline void
gdk_color_finish (GdkColor *self)
{
if (gdk_color_is_allocated (self))
g_free (self->components);
g_object_unref (gdk_color_get_color_space (self));
}
static inline GdkColorSpace *
gdk_color_get_color_space (const GdkColor *self)
{
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (self->color_space) & ~1);
}
static inline float
gdk_color_get_alpha (const GdkColor *self)
{
return self->alpha;
}
static inline const float *
gdk_color_get_components (const GdkColor *self)
{
if (gdk_color_is_allocated (self))
return self->components;
else
return self->values;
}
static inline gsize
gdk_color_get_n_components (const GdkColor *self)
{
return gdk_color_space_get_n_components (gdk_color_get_color_space (self));
}

227
gdk/gdkcolorspace.c Normal file
View File

@@ -0,0 +1,227 @@
/* gdkcolor_space.c
*
* Copyright 2021 (c) Benjamin Otte
*
* 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/>.
*/
/**
* GdkColorSpace:
*
* `GdkColorSpace` is used to describe color spaces.
*
* Tell developers what a color space is instead of just linking to
* https://en.wikipedia.org/wiki/Color_space
*
* `GdkColorSpace` objects are immutable and therefore threadsafe.
*
* Since: 4.10
*/
#include "config.h"
#include "gdkcolorspaceprivate.h"
#include <glib/gi18n-lib.h>
enum {
PROP_0,
N_PROPS
};
//static GParamSpec *properties[N_PROPS];
G_DEFINE_TYPE (GdkColorSpace, gdk_color_space, G_TYPE_OBJECT)
static gboolean
gdk_color_space_default_supports_format (GdkColorSpace *self,
GdkMemoryFormat format)
{
return FALSE;
}
static GBytes *
gdk_color_space_default_save_to_icc_profile (GdkColorSpace *self,
GError **error)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("This color space does not support ICC profiles"));
return NULL;
}
static int
gdk_color_space_default_get_n_components (GdkColorSpace *self)
{
return 0;
}
static gboolean
gdk_color_space_default_equal (GdkColorSpace *profile1,
GdkColorSpace *profile2)
{
return profile1 == profile2;
}
static void
gdk_color_space_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
//GdkColorSpace *self = GDK_COLOR_SPACE (gobject);
switch (prop_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
gdk_color_space_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
//GdkColorSpace *self = GDK_COLOR_SPACE (gobject);
switch (prop_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
gdk_color_space_dispose (GObject *object)
{
//GdkColorSpace *self = GDK_COLOR_SPACE (object);
G_OBJECT_CLASS (gdk_color_space_parent_class)->dispose (object);
}
static void
gdk_color_space_class_init (GdkColorSpaceClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
klass->supports_format = gdk_color_space_default_supports_format;
klass->save_to_icc_profile = gdk_color_space_default_save_to_icc_profile;
klass->get_n_components = gdk_color_space_default_get_n_components;
klass->equal = gdk_color_space_default_equal;
gobject_class->set_property = gdk_color_space_set_property;
gobject_class->get_property = gdk_color_space_get_property;
gobject_class->dispose = gdk_color_space_dispose;
}
static void
gdk_color_space_init (GdkColorSpace *self)
{
}
/**
* gdk_color_space_supports_format:
* @self: a `GdkColorSpace`
* @format: the format to check
*
* Checks if this color space can be used with textures in the given format.
*
* Returns: %TRUE if this colorspace supports the format
*
* Since: 4.10
**/
gboolean
gdk_color_space_supports_format (GdkColorSpace *self,
GdkMemoryFormat format)
{
g_return_val_if_fail (GDK_IS_COLOR_SPACE (self), FALSE);
g_return_val_if_fail (format < GDK_MEMORY_N_FORMATS, FALSE);
return GDK_COLOR_SPACE_GET_CLASS (self)->supports_format (self, format);
}
/**
* gdk_color_space_save_to_icc_profile:
* @self: a `GdkColorSpace`
* @error: Return location for an error
*
* Saves the color space to an
* [ICC profile](https://en.wikipedia.org/wiki/ICC_profile).
*
* Some color spaces cannot be represented as ICC profiles. In
* that case, an error will be set and %NULL will be returned.
*
* Returns: (nullable): A new `GBytes` containing the ICC profile
*
* Since: 4.10
**/
GBytes *
gdk_color_space_save_to_icc_profile (GdkColorSpace *self,
GError **error)
{
g_return_val_if_fail (GDK_IS_COLOR_SPACE (self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return GDK_COLOR_SPACE_GET_CLASS (self)->save_to_icc_profile (self, error);
}
gboolean
gdk_color_space_is_linear (GdkColorSpace *self)
{
g_return_val_if_fail (GDK_IS_COLOR_SPACE (self), FALSE);
return self == gdk_color_space_get_srgb_linear ();
}
int
gdk_color_space_get_n_components (GdkColorSpace *self)
{
g_return_val_if_fail (GDK_IS_COLOR_SPACE (self), 0);
return GDK_COLOR_SPACE_GET_CLASS (self)->get_n_components (self);
}
/**
* gdk_color_space_equal:
* @profile1: (type GdkColorSpace): a `GdkColorSpace`
* @profile2: (type GdkColorSpace): another `GdkColorSpace`
*
* Compares two `GdkColorSpace`s for equality.
*
* Note that this function is not guaranteed to be perfect and two objects
* describing the same color space may compare not equal. However, different
* color spaces will never compare equal.
*
* Returns: %TRUE if the two color profiles compare equal
*
* Since: 4.10
*/
gboolean
gdk_color_space_equal (gconstpointer profile1,
gconstpointer profile2)
{
if (profile1 == profile2)
return TRUE;
if (GDK_COLOR_SPACE_GET_CLASS (profile1) == GDK_COLOR_SPACE_GET_CLASS (profile2))
return GDK_COLOR_SPACE_GET_CLASS (profile1)->equal (GDK_COLOR_SPACE (profile1),
GDK_COLOR_SPACE (profile2));
return FALSE;
}

81
gdk/gdkcolorspace.h Normal file
View File

@@ -0,0 +1,81 @@
/* gdkcolor_space.h
*
* Copyright 2021 (c) Benjamin Otte
*
* 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/>.
*/
#ifndef __GDK_COLOR_SPACE_H__
#define __GDK_COLOR_SPACE_H__
#if !defined (__GDK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gdk/gdk.h> can be included directly."
#endif
#include <gdk/gdkversionmacros.h>
#include <gdk/gdktypes.h>
#include <gdk/gdkenums.h>
G_BEGIN_DECLS
#define GDK_TYPE_COLOR_SPACE (gdk_color_space_get_type ())
#define GDK_COLOR_SPACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_COLOR_SPACE, GdkColorSpace))
#define GDK_IS_COLOR_SPACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_COLOR_SPACE))
#define GDK_COLOR_SPACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_COLOR_SPACE, GdkColorSpaceClass))
#define GDK_IS_COLOR_SPACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_COLOR_SPACE))
#define GDK_COLOR_SPACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_COLOR_SPACE, GdkColorSpaceClass))
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkColorSpace, g_object_unref)
typedef struct _GdkColorSpaceClass GdkColorSpaceClass;
GDK_AVAILABLE_IN_4_10
GType gdk_color_space_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_4_10
GdkColorSpace * gdk_color_space_get_srgb (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_4_10
GdkColorSpace * gdk_color_space_new_from_icc_profile (GBytes *icc_profile,
GError **error);
GDK_AVAILABLE_IN_4_10
GdkColorSpace * gdk_color_space_new_from_cicp (int color_primaries,
int transfer_characteristics,
int matrix_coefficients,
gboolean full_range);
GDK_AVAILABLE_IN_4_10
gboolean gdk_color_space_supports_format (GdkColorSpace *self,
GdkMemoryFormat format);
GDK_AVAILABLE_IN_4_10
GBytes * gdk_color_space_save_to_icc_profile (GdkColorSpace *self,
GError **error);
GDK_AVAILABLE_IN_4_10
gboolean gdk_color_space_is_linear (GdkColorSpace *self);
GDK_AVAILABLE_IN_4_10
int gdk_color_space_get_n_components (GdkColorSpace *self);
GDK_AVAILABLE_IN_4_10
gboolean gdk_color_space_equal (gconstpointer profile1,
gconstpointer profile2);
G_END_DECLS
#endif /* __GDK_COLOR_SPACE_H__ */

View File

@@ -0,0 +1,39 @@
#ifndef __GDK_COLOR_SPACE_PRIVATE_H__
#define __GDK_COLOR_SPACE_PRIVATE_H__
#include "gdkcolorspace.h"
#include <lcms2.h>
G_BEGIN_DECLS
struct _GdkColorSpace
{
GObject parent_instance;
};
struct _GdkColorSpaceClass
{
GObjectClass parent_class;
gboolean (* supports_format) (GdkColorSpace *self,
GdkMemoryFormat format);
GBytes * (* save_to_icc_profile) (GdkColorSpace *self,
GError **error);
int (* get_n_components) (GdkColorSpace *self);
gboolean (* equal) (GdkColorSpace *profile1,
GdkColorSpace *profile2);
};
GdkColorSpace * gdk_color_space_get_srgb_linear (void) G_GNUC_CONST;
cmsHTRANSFORM * gdk_color_space_lookup_transform (GdkColorSpace *source,
guint source_type,
GdkColorSpace *dest,
guint dest_type);
G_END_DECLS
#endif /* __GDK_COLOR_SPACE_PRIVATE_H__ */

View File

@@ -51,6 +51,7 @@ typedef enum {
GDK_DEBUG_VULKAN_VALIDATE = 1 << 23,
GDK_DEBUG_DEFAULT_SETTINGS= 1 << 24,
GDK_DEBUG_HIGH_DEPTH = 1 << 25,
GDK_DEBUG_SRGB = 1 << 26,
} GdkDebugFlags;
extern guint _gdk_debug_flags;

View File

@@ -23,6 +23,7 @@
#include "gdkdisplayprivate.h"
#include "gdkglcontextprivate.h"
#include "gdkmemoryformatprivate.h"
#include "gdkcolorspace.h"
#include "gdkmemorytextureprivate.h"
#include "gdktextureprivate.h"
@@ -38,6 +39,7 @@ struct _GdkGLTexture {
GdkTexture parent_instance;
GdkGLContext *context;
GdkGLTextureFlags flags;
guint id;
GdkTexture *saved;
@@ -115,6 +117,7 @@ typedef struct _Download Download;
struct _Download
{
GdkMemoryFormat format;
GdkColorSpace *color_space;
guchar *data;
gsize stride;
};
@@ -157,7 +160,7 @@ gdk_gl_texture_do_download (gpointer texture_,
expected_stride = texture->width * gdk_memory_format_bytes_per_pixel (download->format);
if (download->stride == expected_stride &&
!gdk_gl_context_get_use_es (self->context) &&
!gdk_gl_context_get_use_es (self->context) &&
gdk_memory_format_gl_format (download->format, TRUE, &gl_internal_format, &gl_format, &gl_type))
{
glGetTexImage (GL_TEXTURE_2D,
@@ -193,7 +196,7 @@ gdk_gl_texture_do_download (gpointer texture_,
(download->stride == expected_stride))
{
glReadPixels (0, 0,
texture->width, texture->height,
texture->width, texture->height,
gl_read_format,
gl_read_type,
download->data);
@@ -204,7 +207,7 @@ gdk_gl_texture_do_download (gpointer texture_,
guchar *pixels = g_malloc_n (texture->width * actual_bpp, texture->height);
glReadPixels (0, 0,
texture->width, texture->height,
texture->width, texture->height,
gl_read_format,
gl_read_type,
pixels);
@@ -212,9 +215,11 @@ gdk_gl_texture_do_download (gpointer texture_,
gdk_memory_convert (download->data,
download->stride,
download->format,
download->color_space,
pixels,
texture->width * actual_bpp,
actual_format,
gdk_color_space_get_srgb (),
texture->width,
texture->height);
@@ -228,6 +233,7 @@ gdk_gl_texture_do_download (gpointer texture_,
static void
gdk_gl_texture_download (GdkTexture *texture,
GdkMemoryFormat format,
GdkColorSpace *color_space,
guchar *data,
gsize stride)
{
@@ -236,11 +242,12 @@ gdk_gl_texture_download (GdkTexture *texture,
if (self->saved)
{
gdk_texture_do_download (self->saved, format, data, stride);
gdk_texture_do_download (self->saved, format, color_space, data, stride);
return;
}
download.format = format;
download.color_space = color_space;
download.data = data;
download.stride = stride;
@@ -275,6 +282,12 @@ gdk_gl_texture_get_id (GdkGLTexture *self)
return self->id;
}
GdkGLTextureFlags
gdk_gl_texture_get_flags (GdkGLTexture *self)
{
return self->flags;
}
/**
* gdk_gl_texture_release:
* @self: a `GdkTexture` wrapping a GL texture
@@ -295,7 +308,8 @@ gdk_gl_texture_release (GdkGLTexture *self)
texture = GDK_TEXTURE (self);
self->saved = GDK_TEXTURE (gdk_memory_texture_from_texture (texture,
gdk_texture_get_format (texture)));
gdk_texture_get_format (texture),
gdk_texture_get_color_space (texture)));
if (self->destroy)
{
@@ -428,6 +442,10 @@ gdk_gl_texture_determine_format (GdkGLTexture *self)
* which will happen when the GdkTexture object is finalized, or due to
* an explicit call of [method@Gdk.GLTexture.release].
*
* The texture data is assumed to be premultiplied, not flipped, and in the
* sRGB colorspace, see [ctor@Gdk.GLTexture.new_with_color_profile] to override
* this.
*
* Return value: (transfer full) (type GdkGLTexture): A newly-created
* `GdkTexture`
*/
@@ -438,6 +456,46 @@ gdk_gl_texture_new (GdkGLContext *context,
int height,
GDestroyNotify destroy,
gpointer data)
{
return gdk_gl_texture_new_with_color_space (context, id,
width, height,
GDK_GL_TEXTURE_PREMULTIPLIED,
gdk_color_space_get_srgb (),
destroy, data);
}
/**
* gdk_gl_texture_new_with_color_space:
* @context: a `GdkGLContext`
* @id: the ID of a texture that was created with @context
* @width: the nominal width of the texture
* @height: the nominal height of the texture
* @flags: flags that describe the content of the texture
* @color_space: the `GdkColorSpace` for the content of the texture
* @destroy: a destroy notify that will be called when the GL resources
* are released
* @data: data that gets passed to @destroy
*
* Creates a new texture for an existing GL texture with a given color space
* and flags.
*
* Note that the GL texture must not be modified until @destroy is called,
* which will happen when the `GdkTexture` object is finalized, or due to
* an explicit call of [method@Gdk.GLTexture.release].
*
* Return value: (transfer full): A newly-created `GdkTexture`
*
* Since: 4.10
*/
GdkTexture *
gdk_gl_texture_new_with_color_space (GdkGLContext *context,
guint id,
int width,
int height,
GdkGLTextureFlags flags,
GdkColorSpace *color_space,
GDestroyNotify destroy,
gpointer data)
{
GdkGLTexture *self;
@@ -449,10 +507,12 @@ gdk_gl_texture_new (GdkGLContext *context,
self = g_object_new (GDK_TYPE_GL_TEXTURE,
"width", width,
"height", height,
"color-space", color_space,
NULL);
self->context = g_object_ref (context);
self->id = id;
self->flags = flags;
self->destroy = destroy;
self->data = data;
@@ -460,4 +520,3 @@ gdk_gl_texture_new (GdkGLContext *context,
return GDK_TEXTURE (self);
}

View File

@@ -47,6 +47,29 @@ GdkTexture * gdk_gl_texture_new (GdkGLContext
GDestroyNotify destroy,
gpointer data);
/**
* GdkGLTextureFlags:
* @GDK_GL_TEXTURE_FLAGS_PREMULTIPLIED: The alpha in the data is premultiplied
* @GDK_GL_TEXTURE_FLAGS_FLIPPED: The data has the origin at the bottom (this is usually
* th case for textures that are produced by GL rendering)
*
* Flags that describe the content of a GL texture.
*/
typedef enum {
GDK_GL_TEXTURE_PREMULTIPLIED = 1 << 0,
GDK_GL_TEXTURE_FLIPPED = 1 << 1,
} GdkGLTextureFlags;
GDK_AVAILABLE_IN_4_10
GdkTexture * gdk_gl_texture_new_with_color_space (GdkGLContext *context,
guint id,
int width,
int height,
GdkGLTextureFlags flags,
GdkColorSpace *color_space,
GDestroyNotify destroy,
gpointer data);
GDK_AVAILABLE_IN_ALL
void gdk_gl_texture_release (GdkGLTexture *self);

View File

@@ -9,6 +9,7 @@ G_BEGIN_DECLS
GdkGLContext * gdk_gl_texture_get_context (GdkGLTexture *self);
guint gdk_gl_texture_get_id (GdkGLTexture *self);
GdkGLTextureFlags gdk_gl_texture_get_flags (GdkGLTexture *self);
G_END_DECLS

342
gdk/gdklcmscolorspace.c Normal file
View File

@@ -0,0 +1,342 @@
/* gdklcmscolorspace.c
*
* Copyright 2021 (c) Benjamin Otte
*
* 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/>.
*/
#include "config.h"
#include "gdklcmscolorspaceprivate.h"
#include "gdkcicpcolorspaceprivate.h"
#include <glib/gi18n-lib.h>
struct _GdkLcmsColorSpace
{
GdkColorSpace parent_instance;
cmsHPROFILE lcms_profile;
};
struct _GdkLcmsColorSpaceClass
{
GdkColorSpaceClass parent_class;
};
G_DEFINE_TYPE (GdkLcmsColorSpace, gdk_lcms_color_space, GDK_TYPE_COLOR_SPACE)
static gboolean
gdk_lcms_color_space_supports_format (GdkColorSpace *space,
GdkMemoryFormat format)
{
GdkLcmsColorSpace *self = GDK_LCMS_COLOR_SPACE (space);
return cmsGetColorSpace (self->lcms_profile) == cmsSigRgbData;
}
static GBytes *
gdk_lcms_color_space_save_to_icc_profile (GdkColorSpace *space,
GError **error)
{
GdkLcmsColorSpace *self = GDK_LCMS_COLOR_SPACE (space);
cmsUInt32Number size;
guchar *data;
size = 0;
if (!cmsSaveProfileToMem (self->lcms_profile, NULL, &size))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Could not prepare ICC profile"));
return NULL;
}
data = g_malloc (size);
if (!cmsSaveProfileToMem (self->lcms_profile, data, &size))
{
g_free (data);
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Failed to save ICC profile"));
return NULL;
}
return g_bytes_new_take (data, size);
}
static int
gdk_lcms_color_space_get_n_components (GdkColorSpace *space)
{
return 3;
}
static gboolean
gdk_lcms_color_space_equal (GdkColorSpace *profile1,
GdkColorSpace *profile2)
{
GBytes *icc1, *icc2;
gboolean res;
icc1 = gdk_color_space_save_to_icc_profile (profile1, NULL);
icc2 = gdk_color_space_save_to_icc_profile (profile2, NULL);
res = g_bytes_equal (icc1, icc2);
g_bytes_unref (icc1);
g_bytes_unref (icc2);
return res;
}
static void
gdk_lcms_color_space_dispose (GObject *object)
{
GdkLcmsColorSpace *self = GDK_LCMS_COLOR_SPACE (object);
g_clear_pointer (&self->lcms_profile, cmsCloseProfile);
G_OBJECT_CLASS (gdk_lcms_color_space_parent_class)->dispose (object);
}
static void
gdk_lcms_color_space_class_init (GdkLcmsColorSpaceClass *klass)
{
GdkColorSpaceClass *color_space_class = GDK_COLOR_SPACE_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
color_space_class->supports_format = gdk_lcms_color_space_supports_format;
color_space_class->save_to_icc_profile = gdk_lcms_color_space_save_to_icc_profile;
color_space_class->get_n_components = gdk_lcms_color_space_get_n_components;
color_space_class->equal = gdk_lcms_color_space_equal;
gobject_class->dispose = gdk_lcms_color_space_dispose;
}
static void
gdk_lcms_color_space_init (GdkLcmsColorSpace *self)
{
}
GdkColorSpace *
gdk_lcms_color_space_new_from_lcms_profile (cmsHPROFILE lcms_profile)
{
GdkLcmsColorSpace *result;
result = g_object_new (GDK_TYPE_LCMS_COLOR_SPACE, NULL);
result->lcms_profile = lcms_profile;
return GDK_COLOR_SPACE (result);
}
/**
* gdk_color_space_new_from_icc_profile:
* @icc_profile: The ICC profiles given as a `GBytes`
* @error: Return location for an error
*
* Creates a new color profile for the given ICC profile data.
*
* if the profile is not valid, %NULL is returned and an error
* is raised.
*
* Returns: a new `GdkLcmsColorSpace` or %NULL on error
*
* Since: 4.10
*/
GdkColorSpace *
gdk_color_space_new_from_icc_profile (GBytes *icc_profile,
GError **error)
{
cmsHPROFILE lcms_profile;
const guchar *data;
gsize size;
g_return_val_if_fail (icc_profile != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
data = g_bytes_get_data (icc_profile, &size);
lcms_profile = cmsOpenProfileFromMem (data, size);
if (lcms_profile == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Failed to load ICC profile"));
return NULL;
}
return gdk_lcms_color_space_new_from_lcms_profile (lcms_profile);
}
cmsHPROFILE
gdk_lcms_color_space_get_lcms_profile (GdkColorSpace *self)
{
g_return_val_if_fail (GDK_IS_LCMS_COLOR_SPACE (self), NULL);
return GDK_LCMS_COLOR_SPACE (self)->lcms_profile;
}
/**
* gdk_color_space_get_srgb:
*
* Returns the object representing the sRGB color space.
*
* If you don't know anything about color spaces but need one for
* use with some function, this one is most likely the right one.
*
* Returns: (transfer none): the object for the sRGB color space.
*
* Since: 4.8
*/
GdkColorSpace *
gdk_color_space_get_srgb (void)
{
static GdkColorSpace *srgb_color_space;
if (g_once_init_enter (&srgb_color_space))
{
GdkColorSpace *color_space;
color_space = gdk_lcms_color_space_new_from_lcms_profile (cmsCreate_sRGBProfile ());
g_assert (color_space);
g_once_init_leave (&srgb_color_space, color_space);
}
return srgb_color_space;
}
/*<private>
* gdk_color_space_get_srgb_linear:
*
* Returns the object corresponding to the linear sRGB color space.
*
* It can display the same colors as the sRGB color space, but it
* does not have a gamma curve.
*
* Returns: (transfer none): the object for the linear sRGB color space.
*
* Since: 4.8
*/
GdkColorSpace *
gdk_color_space_get_srgb_linear (void)
{
static GdkColorSpace *srgb_linear_color_space;
if (g_once_init_enter (&srgb_linear_color_space))
{
cmsToneCurve *curve;
cmsHPROFILE lcms_profile;
GdkColorSpace *color_space;
curve = cmsBuildGamma (NULL, 1.0);
lcms_profile = cmsCreateRGBProfile (&(cmsCIExyY) {
0.3127, 0.3290, 1.0
},
&(cmsCIExyYTRIPLE) {
{ 0.6400, 0.3300, 1.0 },
{ 0.3000, 0.6000, 1.0 },
{ 0.1500, 0.0600, 1.0 }
},
(cmsToneCurve*[3]) { curve, curve, curve });
cmsFreeToneCurve (curve);
color_space = gdk_lcms_color_space_new_from_lcms_profile (lcms_profile);
g_assert (color_space);
g_once_init_leave (&srgb_linear_color_space, color_space);
}
return srgb_linear_color_space;
}
typedef struct _GdkColorTransformCache GdkColorTransformCache;
struct _GdkColorTransformCache
{
GdkColorSpace *source;
guint source_type;
GdkColorSpace *dest;
guint dest_type;
};
static void
gdk_color_transform_cache_free (gpointer data)
{
g_free (data);
}
static guint
gdk_color_transform_cache_hash (gconstpointer data)
{
const GdkColorTransformCache *cache = data;
return g_direct_hash (cache->source) ^
(g_direct_hash (cache->dest) >> 2) ^
((cache->source_type << 16) | (cache->source_type >> 16)) ^
cache->dest_type;
}
static gboolean
gdk_color_transform_cache_equal (gconstpointer data1,
gconstpointer data2)
{
const GdkColorTransformCache *cache1 = data1;
const GdkColorTransformCache *cache2 = data2;
return cache1->source == cache2->source &&
cache1->source_type == cache2->source_type &&
cache1->dest == cache2->dest &&
cache1->dest_type == cache2->dest_type;
}
cmsHTRANSFORM *
gdk_color_space_lookup_transform (GdkColorSpace *source,
guint source_type,
GdkColorSpace *dest,
guint dest_type)
{
GdkColorTransformCache *entry;
static GHashTable *cache = NULL;
cmsHTRANSFORM *transform;
if (GDK_IS_CICP_COLOR_SPACE (dest))
dest = gdk_cicp_color_space_get_lcms_color_space (dest);
if (GDK_IS_CICP_COLOR_SPACE (source))
source = gdk_cicp_color_space_get_lcms_color_space (source);
if (cache == NULL)
cache = g_hash_table_new_full (gdk_color_transform_cache_hash,
gdk_color_transform_cache_equal,
gdk_color_transform_cache_free,
cmsDeleteTransform);
transform = g_hash_table_lookup (cache,
&(GdkColorTransformCache) {
source, source_type,
dest, dest_type
});
if (G_UNLIKELY (transform == NULL && source && dest))
{
transform = cmsCreateTransform (gdk_lcms_color_space_get_lcms_profile (source),
source_type,
gdk_lcms_color_space_get_lcms_profile (dest),
dest_type,
INTENT_PERCEPTUAL,
cmsFLAGS_COPY_ALPHA);
entry = g_new (GdkColorTransformCache, 1);
*entry = (GdkColorTransformCache) {
source, source_type,
dest, dest_type
};
g_hash_table_insert (cache, entry, transform);
}
return transform;
}

View File

@@ -0,0 +1,48 @@
/* gdklcmscolorspace.h
*
* Copyright 2021 (c) Benjamin Otte
*
* 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/>.
*/
#ifndef __GDK_LCMS_COLOR_SPACE_PRIVATE_H__
#define __GDK_LCMS_COLOR_SPACE_PRIVATE_H__
#include "gdkcolorspaceprivate.h"
#include <lcms2.h>
G_BEGIN_DECLS
#define GDK_TYPE_LCMS_COLOR_SPACE (gdk_lcms_color_space_get_type ())
#define GDK_LCMS_COLOR_SPACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_LCMS_COLOR_SPACE, GdkLcmsColorSpace))
#define GDK_IS_LCMS_COLOR_SPACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_LCMS_COLOR_SPACE))
#define GDK_LCMS_COLOR_SPACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_LCMS_COLOR_SPACE, GdkLcmsColorSpaceClass))
#define GDK_IS_LCMS_COLOR_SPACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_LCMS_COLOR_SPACE))
#define GDK_LCMS_COLOR_SPACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_LCMS_COLOR_SPACE, GdkLcmsColorSpaceClass))
typedef struct _GdkLcmsColorSpace GdkLcmsColorSpace;
typedef struct _GdkLcmsColorSpaceClass GdkLcmsColorSpaceClass;
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkLcmsColorSpace, g_object_unref)
GType gdk_lcms_color_space_get_type (void) G_GNUC_CONST;
GdkColorSpace * gdk_lcms_color_space_new_from_lcms_profile (cmsHPROFILE lcms_profile);
cmsHPROFILE gdk_lcms_color_space_get_lcms_profile (GdkColorSpace *color_space);
G_END_DECLS
#endif /* __GDK_LCMS_COLOR_SPACE_PRIVATE_H__ */

View File

@@ -21,6 +21,9 @@
#include "gdkmemoryformatprivate.h"
#include "gdklcmscolorspaceprivate.h"
#include "gdkcicpcolorspaceprivate.h"
#include "gdkprofilerprivate.h"
#include "gsk/gl/fp16private.h"
#include <epoxy/gl.h>
@@ -72,6 +75,223 @@ TYPED_FUNCS (b8g8r8, guchar, 2, 1, 0, -1, 3, 255)
TYPED_FUNCS (r16g16b16, guint16, 0, 1, 2, -1, 6, 65535)
TYPED_FUNCS (r16g16b16a16, guint16, 0, 1, 2, 3, 8, 65535)
#define PREMULTIPLY_FUNCS(type, A, scale) \
static void \
type ## _premultiply_ ## A (guchar *dest_data, \
const guchar *src_data, \
gsize n) \
{ \
type *dest = (type *) dest_data; \
const type *src = (const type *) src_data; \
for (gsize i = 0; i < n; i++) \
{ \
unsigned a = src[A]; \
if (A != 0) dest[0] = src[0] * a / scale; \
if (A != 1) dest[1] = src[1] * a / scale; \
if (A != 2) dest[2] = src[2] * a / scale; \
if (A != 3) dest[3] = src[3] * a / scale; \
dest[A] = a; \
src += 4; \
dest += 4; \
} \
} \
\
static void \
type ## _unpremultiply_ ## A (guchar *dest_data, \
const guchar *src_data, \
gsize n) \
{ \
type *dest = (type *) dest_data; \
const type *src = (const type *) src_data; \
for (gsize i = 0; i < n; i++) \
{ \
unsigned a = src[A]; \
if (a == 0) \
{ \
dest[0] = dest[1] = dest[2] = dest[3] = 0; \
} \
else \
{ \
if (A != 0) dest[0] = (src[0] * scale + a / 2) / a; \
if (A != 1) dest[1] = (src[1] * scale + a / 2) / a; \
if (A != 2) dest[2] = (src[2] * scale + a / 2) / a; \
if (A != 3) dest[3] = (src[3] * scale + a / 2) / a; \
dest[A] = a; \
} \
src += 4; \
dest += 4; \
} \
}
PREMULTIPLY_FUNCS (guint8, 0, G_MAXUINT8)
PREMULTIPLY_FUNCS (guint8, 3, G_MAXUINT8)
PREMULTIPLY_FUNCS (guint16, 3, G_MAXUINT16)
static void
half_float_premultiply (guchar *dest_data,
const guchar *src_data,
gsize n)
{
guint16 *dest = (guint16 *) dest_data;
const float *src = (const float *) src_data;
for (gsize i = 0; i < n; i++)
{
float tmp[4];
tmp[0] = src[0] * src[3];
tmp[1] = src[1] * src[3];
tmp[2] = src[2] * src[3];
tmp[3] = src[3];
float_to_half4 (tmp, dest);
dest += 4;
src += 4;
}
}
static void
half_float_unpremultiply (guchar *dest_data,
const guchar *src_data,
gsize n)
{
float *dest = (float *) dest_data;
const guint16 *src = (const guint16 *) src_data;
for (gsize i = 0; i < n; i++)
{
half_to_float4 (src, dest);
if (dest[3] <= 1/255.0)
{
memset (dest, 0, sizeof (guint) * 4);
}
else
{
dest[0] = dest[0] / dest[3];
dest[1] = dest[1] / dest[3];
dest[2] = dest[2] / dest[3];
}
dest += 4;
src += 4;
}
}
static void
float_premultiply (guchar *dest_data,
const guchar *src_data,
gsize n)
{
float *dest = (float *) dest_data;
const float *src = (const float *) src_data;
for (gsize i = 0; i < n; i++)
{
float a = src[3];
dest[0] = src[0] * a;
dest[1] = src[1] * a;
dest[2] = src[2] * a;
dest[3] = a;
dest += 4;
src += 4;
}
}
static void
float_unpremultiply (guchar *dest_data,
const guchar *src_data,
gsize n)
{
float *dest = (float *) dest_data;
const float *src = (const float *) src_data;
for (gsize i = 0; i < n; i++)
{
float a = src[3];
if (a <= 1/255.0)
{
memset (dest, 0, sizeof (float) * 4);
}
else
{
dest[0] = src[0] / a;
dest[1] = src[1] / a;
dest[2] = src[2] / a;
dest[3] = a;
}
dest += 4;
src += 4;
}
}
#define COMPRESS_FUNCS(type, scale) \
static void \
type ## _expand (guchar *dest_data, \
const guchar *src_data, \
gsize n) \
{ \
type *dest = (type *) dest_data; \
const type *src = (const type *) src_data; \
for (gsize i = 0; i < n; i++) \
{ \
dest[0] = src[0]; \
dest[1] = src[1]; \
dest[2] = src[2]; \
dest[3] = scale; \
dest += 4; \
src += 3; \
} \
} \
\
static void \
type ## _compress (guchar *dest_data, \
const guchar *src_data, \
gsize n) \
{ \
type *dest = (type *) dest_data; \
const type *src = (const type *) src_data; \
for (gsize i = 0; i < n; i++) \
{ \
dest[0] = src[0] * src[3] / scale; \
dest[1] = src[1] * src[3] / scale; \
dest[2] = src[2] * src[3] / scale; \
dest += 3; \
src += 4; \
} \
}
COMPRESS_FUNCS (guint8, G_MAXUINT8)
COMPRESS_FUNCS (guint16, G_MAXUINT16)
COMPRESS_FUNCS (float, 1.0f)
static void
half_float_expand (guchar *dest_data,
const guchar *src_data,
gsize n)
{
float *dest = (float *) dest_data;
const guint16 *src = (const guint16 *) src_data;
for (gsize i = 0; i < n; i++)
{
half_to_float (src, dest, 3);
dest[3] = 1.0;
dest += 4;
src += 3;
}
}
static void
half_float_compress (guchar *dest_data,
const guchar *src_data,
gsize n)
{
guint16 *dest = (guint16 *) dest_data;
const float *src = (const float *) src_data;
for (gsize i = 0; i < n; i++)
{
float tmp[3];
tmp[0] = src[0] * src[3];
tmp[1] = src[1] * src[3];
tmp[2] = src[2] * src[3];
float_to_half (tmp, dest, 3);
dest += 3;
src += 4;
}
}
static void
r16g16b16_float_to_float (float *dest,
const guchar *src_data,
@@ -166,56 +386,9 @@ r32g32b32a32_float_from_float (guchar *dest,
memcpy (dest, src, sizeof (float) * n * 4);
}
#define PREMULTIPLY_FUNC(name, R1, G1, B1, A1, R2, G2, B2, A2) \
static void \
name (guchar *dest, \
const guchar *src, \
gsize n) \
{ \
for (; n > 0; n--) \
{ \
guchar a = src[A1]; \
guint16 r = (guint16)src[R1] * a + 127; \
guint16 g = (guint16)src[G1] * a + 127; \
guint16 b = (guint16)src[B1] * a + 127; \
dest[R2] = (r + (r >> 8) + 1) >> 8; \
dest[G2] = (g + (g >> 8) + 1) >> 8; \
dest[B2] = (b + (b >> 8) + 1) >> 8; \
dest[A2] = a; \
dest += 4; \
src += 4; \
} \
}
PREMULTIPLY_FUNC(r8g8b8a8_to_r8g8b8a8_premultiplied, 0, 1, 2, 3, 0, 1, 2, 3)
PREMULTIPLY_FUNC(r8g8b8a8_to_b8g8r8a8_premultiplied, 0, 1, 2, 3, 2, 1, 0, 3)
PREMULTIPLY_FUNC(r8g8b8a8_to_a8r8g8b8_premultiplied, 0, 1, 2, 3, 1, 2, 3, 0)
PREMULTIPLY_FUNC(r8g8b8a8_to_a8b8g8r8_premultiplied, 0, 1, 2, 3, 3, 2, 1, 0)
#define ADD_ALPHA_FUNC(name, R1, G1, B1, R2, G2, B2, A2) \
static void \
name (guchar *dest, \
const guchar *src, \
gsize n) \
{ \
for (; n > 0; n--) \
{ \
dest[R2] = src[R1]; \
dest[G2] = src[G1]; \
dest[B2] = src[B1]; \
dest[A2] = 255; \
dest += 4; \
src += 3; \
} \
}
ADD_ALPHA_FUNC(r8g8b8_to_r8g8b8a8, 0, 1, 2, 0, 1, 2, 3)
ADD_ALPHA_FUNC(r8g8b8_to_b8g8r8a8, 0, 1, 2, 2, 1, 0, 3)
ADD_ALPHA_FUNC(r8g8b8_to_a8r8g8b8, 0, 1, 2, 1, 2, 3, 0)
ADD_ALPHA_FUNC(r8g8b8_to_a8b8g8r8, 0, 1, 2, 3, 2, 1, 0)
struct _GdkMemoryFormatDescription
{
const char *name;
GdkMemoryAlpha alpha;
gsize bytes_per_pixel;
gsize alignment;
@@ -229,6 +402,12 @@ struct _GdkMemoryFormatDescription
/* no premultiplication going on here */
void (* to_float) (float *, const guchar*, gsize);
void (* from_float) (guchar *, const float *, gsize);
struct {
guint type;
gsize bpp;
} lcms;
void (* to_lcms) (guchar *, const guchar *, gsize);
void (* from_lcms) (guchar *, const guchar *, gsize);
};
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
@@ -241,6 +420,7 @@ struct _GdkMemoryFormatDescription
static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
[GDK_MEMORY_B8G8R8A8_PREMULTIPLIED] = {
"B8G8R8A8_PREMULTIPLIED",
GDK_MEMORY_ALPHA_PREMULTIPLIED,
4,
G_ALIGNOF (guchar),
@@ -249,8 +429,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE },
b8g8r8a8_premultiplied_to_float,
b8g8r8a8_premultiplied_from_float,
{ TYPE_BGRA_8, 4 },
guint8_unpremultiply_3,
guint8_premultiply_3,
},
[GDK_MEMORY_A8R8G8B8_PREMULTIPLIED] = {
"A8R8G8B8_PREMULTIPLIED",
GDK_MEMORY_ALPHA_PREMULTIPLIED,
4,
G_ALIGNOF (guchar),
@@ -259,8 +443,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGBA8, GL_BGRA, GDK_GL_UNSIGNED_BYTE_FLIPPED },
a8r8g8b8_premultiplied_to_float,
a8r8g8b8_premultiplied_from_float,
{ TYPE_ARGB_8, 4 },
guint8_unpremultiply_0,
guint8_premultiply_0,
},
[GDK_MEMORY_R8G8B8A8_PREMULTIPLIED] = {
"R8G8B8A8_PREMULTIPLIED",
GDK_MEMORY_ALPHA_PREMULTIPLIED,
4,
G_ALIGNOF (guchar),
@@ -269,8 +457,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE },
r8g8b8a8_premultiplied_to_float,
r8g8b8a8_premultiplied_from_float,
{ TYPE_RGBA_8, 4 },
guint8_unpremultiply_3,
guint8_premultiply_3,
},
[GDK_MEMORY_B8G8R8A8] = {
"B8G8R8A8",
GDK_MEMORY_ALPHA_STRAIGHT,
4,
G_ALIGNOF (guchar),
@@ -279,8 +471,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE },
b8g8r8a8_to_float,
b8g8r8a8_from_float,
{ TYPE_BGRA_8, 4 },
NULL,
NULL,
},
[GDK_MEMORY_A8R8G8B8] = {
"A8R8G8B8",
GDK_MEMORY_ALPHA_STRAIGHT,
4,
G_ALIGNOF (guchar),
@@ -289,8 +485,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGBA8, GL_RGBA, GDK_GL_UNSIGNED_BYTE_FLIPPED },
a8r8g8b8_to_float,
a8r8g8b8_from_float,
{ TYPE_ARGB_8, 4 },
NULL,
NULL,
},
[GDK_MEMORY_R8G8B8A8] = {
"R8G8B8A8",
GDK_MEMORY_ALPHA_STRAIGHT,
4,
G_ALIGNOF (guchar),
@@ -299,8 +499,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE },
r8g8b8a8_to_float,
r8g8b8a8_from_float,
{ TYPE_RGBA_8, 4 },
NULL,
NULL,
},
[GDK_MEMORY_A8B8G8R8] = {
"A8B8G8R8",
GDK_MEMORY_ALPHA_STRAIGHT,
4,
G_ALIGNOF (guchar),
@@ -309,8 +513,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGBA8, GL_BGRA, GDK_GL_UNSIGNED_BYTE_FLIPPED },
a8b8g8r8_to_float,
a8b8g8r8_from_float,
{ TYPE_ABGR_8, 4 },
NULL,
NULL,
},
[GDK_MEMORY_R8G8B8] = {
"R8G8B8",
GDK_MEMORY_ALPHA_OPAQUE,
3,
G_ALIGNOF (guchar),
@@ -319,8 +527,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE },
r8g8b8_to_float,
r8g8b8_from_float,
{ TYPE_RGBA_8, 4 },
guint8_expand,
guint8_compress,
},
[GDK_MEMORY_B8G8R8] = {
"B8G8R8",
GDK_MEMORY_ALPHA_OPAQUE,
3,
G_ALIGNOF (guchar),
@@ -329,8 +541,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE },
b8g8r8_to_float,
b8g8r8_from_float,
{ TYPE_BGRA_8, 4 },
guint8_expand,
guint8_compress,
},
[GDK_MEMORY_R16G16B16] = {
"R16G16B16",
GDK_MEMORY_ALPHA_OPAQUE,
6,
G_ALIGNOF (guint16),
@@ -339,8 +555,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGB16, GL_RGB, GL_UNSIGNED_SHORT },
r16g16b16_to_float,
r16g16b16_from_float,
{ TYPE_RGBA_16, 8 },
guint16_expand,
guint16_compress,
},
[GDK_MEMORY_R16G16B16A16_PREMULTIPLIED] = {
"R16G16B16A16_PREMULTIPLIED",
GDK_MEMORY_ALPHA_PREMULTIPLIED,
8,
G_ALIGNOF (guint16),
@@ -349,8 +569,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT },
r16g16b16a16_to_float,
r16g16b16a16_from_float,
{ TYPE_RGBA_16, 8 },
guint16_unpremultiply_3,
guint16_premultiply_3,
},
[GDK_MEMORY_R16G16B16A16] = {
"R16G16B16A16",
GDK_MEMORY_ALPHA_STRAIGHT,
8,
G_ALIGNOF (guint16),
@@ -359,8 +583,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT },
r16g16b16a16_to_float,
r16g16b16a16_from_float,
{ TYPE_RGBA_16, 8 },
NULL,
NULL,
},
[GDK_MEMORY_R16G16B16_FLOAT] = {
"R16G16B16_FLOAT",
GDK_MEMORY_ALPHA_OPAQUE,
6,
G_ALIGNOF (guint16),
@@ -369,8 +597,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGB16F, GL_RGB, GL_HALF_FLOAT },
r16g16b16_float_to_float,
r16g16b16_float_from_float,
{ TYPE_RGBA_FLT, 16 },
half_float_expand,
half_float_compress,
},
[GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED] = {
"R16G16B16A16_FLOAT_PREMULTIPLIED",
GDK_MEMORY_ALPHA_PREMULTIPLIED,
8,
G_ALIGNOF (guint16),
@@ -379,8 +611,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT },
r16g16b16a16_float_to_float,
r16g16b16a16_float_from_float,
{ TYPE_RGBA_FLT, 16 },
half_float_unpremultiply,
half_float_premultiply,
},
[GDK_MEMORY_R16G16B16A16_FLOAT] = {
"R16G16B16A16_FLOAT",
GDK_MEMORY_ALPHA_STRAIGHT,
8,
G_ALIGNOF (guint16),
@@ -389,8 +625,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT },
r16g16b16a16_float_to_float,
r16g16b16a16_float_from_float,
{ TYPE_RGBA_FLT, 16 },
NULL,
NULL
},
[GDK_MEMORY_R32G32B32_FLOAT] = {
"R32G32B32_FLOAT",
GDK_MEMORY_ALPHA_OPAQUE,
12,
G_ALIGNOF (float),
@@ -399,8 +639,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGB32F, GL_RGB, GL_FLOAT },
r32g32b32_float_to_float,
r32g32b32_float_from_float,
{ TYPE_RGBA_FLT, 16 },
float_expand,
float_compress,
},
[GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED] = {
"R32G32B32A32_FLOAT_PREMULTIPLIED",
GDK_MEMORY_ALPHA_PREMULTIPLIED,
16,
G_ALIGNOF (float),
@@ -409,8 +653,12 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGBA32F, GL_RGBA, GL_FLOAT },
r32g32b32a32_float_to_float,
r32g32b32a32_float_from_float,
{ TYPE_RGBA_FLT, 16 },
float_unpremultiply,
float_premultiply,
},
[GDK_MEMORY_R32G32B32A32_FLOAT] = {
"R32G32B32A32_FLOAT",
GDK_MEMORY_ALPHA_STRAIGHT,
16,
G_ALIGNOF (float),
@@ -419,6 +667,9 @@ static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
{ GL_RGBA32F, GL_RGBA, GL_FLOAT },
r32g32b32a32_float_to_float,
r32g32b32a32_float_from_float,
{ TYPE_RGBA_FLT, 16 },
NULL,
NULL,
}
};
@@ -509,86 +760,164 @@ unpremultiply (float *rgba,
}
}
void
gdk_memory_convert (guchar *dest_data,
gsize dest_stride,
GdkMemoryFormat dest_format,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
gsize width,
gsize height)
static void
gdk_memory_convert_same_format (guchar *dest_data,
gsize dest_stride,
const guchar *src_data,
gsize src_stride,
gsize width,
gsize height,
GdkMemoryFormat format)
{
const GdkMemoryFormatDescription *dest_desc = &memory_formats[dest_format];
const GdkMemoryFormatDescription *src_desc = &memory_formats[src_format];
float *tmp;
gsize y;
void (*func) (guchar *, const guchar *, gsize) = NULL;
const GdkMemoryFormatDescription *desc = &memory_formats[format];
gsize y, stride;
g_assert (dest_format < GDK_MEMORY_N_FORMATS);
g_assert (src_format < GDK_MEMORY_N_FORMATS);
if (src_format == GDK_MEMORY_R8G8B8A8 && dest_format == GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
func = r8g8b8a8_to_r8g8b8a8_premultiplied;
else if (src_format == GDK_MEMORY_B8G8R8A8 && dest_format == GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
func = r8g8b8a8_to_b8g8r8a8_premultiplied;
else if (src_format == GDK_MEMORY_R8G8B8A8 && dest_format == GDK_MEMORY_B8G8R8A8_PREMULTIPLIED)
func = r8g8b8a8_to_b8g8r8a8_premultiplied;
else if (src_format == GDK_MEMORY_B8G8R8A8 && dest_format == GDK_MEMORY_B8G8R8A8_PREMULTIPLIED)
func = r8g8b8a8_to_r8g8b8a8_premultiplied;
else if (src_format == GDK_MEMORY_R8G8B8A8 && dest_format == GDK_MEMORY_A8R8G8B8_PREMULTIPLIED)
func = r8g8b8a8_to_a8r8g8b8_premultiplied;
else if (src_format == GDK_MEMORY_B8G8R8A8 && dest_format == GDK_MEMORY_A8R8G8B8_PREMULTIPLIED)
func = r8g8b8a8_to_a8b8g8r8_premultiplied;
else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
func = r8g8b8_to_r8g8b8a8;
else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_R8G8B8A8_PREMULTIPLIED)
func = r8g8b8_to_b8g8r8a8;
else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_B8G8R8A8_PREMULTIPLIED)
func = r8g8b8_to_b8g8r8a8;
else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_B8G8R8A8_PREMULTIPLIED)
func = r8g8b8_to_r8g8b8a8;
else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_A8R8G8B8_PREMULTIPLIED)
func = r8g8b8_to_a8r8g8b8;
else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_A8R8G8B8_PREMULTIPLIED)
func = r8g8b8_to_a8b8g8r8;
else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_R8G8B8A8)
func = r8g8b8_to_r8g8b8a8;
else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_R8G8B8A8)
func = r8g8b8_to_b8g8r8a8;
else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_B8G8R8A8)
func = r8g8b8_to_b8g8r8a8;
else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_B8G8R8A8)
func = r8g8b8_to_r8g8b8a8;
else if (src_format == GDK_MEMORY_R8G8B8 && dest_format == GDK_MEMORY_A8R8G8B8)
func = r8g8b8_to_a8r8g8b8;
else if (src_format == GDK_MEMORY_B8G8R8 && dest_format == GDK_MEMORY_A8R8G8B8)
func = r8g8b8_to_a8b8g8r8;
if (func != NULL)
stride = desc->bytes_per_pixel * width;
if (stride == src_stride && stride == dest_stride)
{
memcpy (dest_data, src_data, stride * height);
}
else
{
for (y = 0; y < height; y++)
{
func (dest_data, src_data, width);
memcpy (dest_data + y * dest_stride,
src_data + y * src_stride,
stride);
}
}
}
static void
gdk_memory_convert_no_transform (guchar *dest_data,
gsize dest_stride,
GdkMemoryFormat dest_format,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
gsize width,
gsize height)
{
if (dest_format == src_format)
{
gdk_memory_convert_same_format (dest_data, dest_stride,
src_data, src_stride,
width, height,
dest_format);
}
else
{
const GdkMemoryFormatDescription *dest_desc = &memory_formats[dest_format];
const GdkMemoryFormatDescription *src_desc = &memory_formats[src_format];
float *tmp;
gsize y;
tmp = g_new (float, width * 4);
for (y = 0; y < height; y++)
{
src_desc->to_float (tmp, src_data, width);
if (src_desc->alpha == GDK_MEMORY_ALPHA_PREMULTIPLIED && dest_desc->alpha == GDK_MEMORY_ALPHA_STRAIGHT)
unpremultiply (tmp, width);
else if (src_desc->alpha == GDK_MEMORY_ALPHA_STRAIGHT && dest_desc->alpha != GDK_MEMORY_ALPHA_STRAIGHT)
premultiply (tmp, width);
dest_desc->from_float (dest_data, tmp, width);
src_data += src_stride;
dest_data += dest_stride;
}
return;
}
tmp = g_new (float, width * 4);
g_free (tmp);
}
}
static void
gdk_memory_convert_transform (guchar *dest_data,
gsize dest_stride,
GdkMemoryFormat dest_format,
GdkColorSpace *dest_color_space,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
GdkColorSpace *src_color_space,
gsize width,
gsize height)
{
const GdkMemoryFormatDescription *dest_desc = &memory_formats[dest_format];
const GdkMemoryFormatDescription *src_desc = &memory_formats[src_format];
cmsHTRANSFORM transform;
guchar *src_tmp, *dest_tmp;
gsize y;
transform = gdk_color_space_lookup_transform (src_color_space,
src_desc->lcms.type,
dest_color_space,
dest_desc->lcms.type);
if (src_desc->to_lcms)
src_tmp = g_malloc_n (src_desc->lcms.bpp, width);
else
src_tmp = NULL;
if (dest_desc->from_lcms)
dest_tmp = g_malloc_n (dest_desc->lcms.bpp, width);
else
dest_tmp = NULL;
for (y = 0; y < height; y++)
{
src_desc->to_float (tmp, src_data, width);
if (src_desc->alpha == GDK_MEMORY_ALPHA_PREMULTIPLIED && dest_desc->alpha == GDK_MEMORY_ALPHA_STRAIGHT)
unpremultiply (tmp, width);
else if (src_desc->alpha == GDK_MEMORY_ALPHA_STRAIGHT && dest_desc->alpha != GDK_MEMORY_ALPHA_STRAIGHT)
premultiply (tmp, width);
dest_desc->from_float (dest_data, tmp, width);
if (src_desc->to_lcms)
src_desc->to_lcms (src_tmp, src_data, width);
cmsDoTransform (transform,
src_tmp ? src_tmp : src_data,
dest_tmp ? dest_tmp : dest_data,
width);
if (dest_desc->from_lcms)
dest_desc->from_lcms (dest_data, dest_tmp, width);
src_data += src_stride;
dest_data += dest_stride;
}
g_free (tmp);
g_free (src_tmp);
g_free (dest_tmp);
}
void
gdk_memory_convert (guchar *dest_data,
gsize dest_stride,
GdkMemoryFormat dest_format,
GdkColorSpace *dest_color_space,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
GdkColorSpace *src_color_space,
gsize width,
gsize height)
{
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
gboolean need_transform;
g_assert (dest_format < GDK_MEMORY_N_FORMATS);
g_assert (src_format < GDK_MEMORY_N_FORMATS);
need_transform = !gdk_color_space_equal (src_color_space, dest_color_space);
if (!need_transform)
{
gdk_memory_convert_no_transform (dest_data, dest_stride, dest_format,
src_data, src_stride, src_format,
width, height);
}
else
{
gdk_memory_convert_transform (dest_data, dest_stride, dest_format, dest_color_space,
src_data, src_stride, src_format, src_color_space,
width, height);
}
gdk_profiler_end_markf (start_time, "memory convert", "%zu pixels %s => %s%s",
width * height,
memory_formats[src_format].name,
memory_formats[dest_format].name,
need_transform ? " transformed" : "");
}

View File

@@ -21,6 +21,7 @@
#define __GDK_MEMORY_CONVERT_PRIVATE_H__
#include "gdkenums.h"
#include "gdkcolorspace.h"
G_BEGIN_DECLS
@@ -43,9 +44,11 @@ gboolean gdk_memory_format_gl_format (GdkMemoryFormat
void gdk_memory_convert (guchar *dest_data,
gsize dest_stride,
GdkMemoryFormat dest_format,
GdkColorSpace *dest_color_space,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
GdkColorSpace *src_color_space,
gsize width,
gsize height);

View File

@@ -21,7 +21,9 @@
#include "gdkmemorytextureprivate.h"
#include "gdkcolorspaceprivate.h"
#include "gdkmemoryformatprivate.h"
#include "gdkcolorspaceprivate.h"
#include "gsk/gl/fp16private.h"
/**
@@ -58,6 +60,7 @@ gdk_memory_texture_dispose (GObject *object)
static void
gdk_memory_texture_download (GdkTexture *texture,
GdkMemoryFormat format,
GdkColorSpace *color_space,
guchar *data,
gsize stride)
{
@@ -65,9 +68,11 @@ gdk_memory_texture_download (GdkTexture *texture,
gdk_memory_convert (data, stride,
format,
color_space,
(guchar *) g_bytes_get_data (self->bytes, NULL),
self->stride,
texture->format,
gdk_texture_get_color_space (texture),
gdk_texture_get_width (texture),
gdk_texture_get_height (texture));
}
@@ -136,6 +141,9 @@ gdk_memory_sanitize (GBytes *bytes,
* The `GBytes` must contain @stride × @height pixels
* in the given format.
*
* This function calls [ctor@Gdk.MemoryTexture.new_with_color_space]
* with the sRGB color space.
*
* Returns: (type GdkMemoryTexture): A newly-created `GdkTexture`
*/
GdkTexture *
@@ -145,18 +153,58 @@ gdk_memory_texture_new (int width,
GBytes *bytes,
gsize stride)
{
GdkMemoryTexture *self;
g_return_val_if_fail (width > 0, NULL);
g_return_val_if_fail (height > 0, NULL);
g_return_val_if_fail (bytes != NULL, NULL);
g_return_val_if_fail (stride >= width * gdk_memory_format_bytes_per_pixel (format), NULL);
return gdk_memory_texture_new_with_color_space (width, height,
format,
gdk_color_space_get_srgb (),
bytes, stride);
}
/**
* gdk_memory_texture_new_with_color_space:
* @width: the width of the texture
* @height: the height of the texture
* @format: the format of the data
* @color_space: a `GdkColorSpace`
* @bytes: the `GBytes` containing the pixel data
* @stride: rowstride for the data
*
* Creates a new texture for a blob of image data with a given color space.
*
* The `GBytes` must contain @stride x @height pixels
* in the given format.
*
* Returns: A newly-created `GdkTexture`
*
* Since: 4.10
*/
GdkTexture *
gdk_memory_texture_new_with_color_space (int width,
int height,
GdkMemoryFormat format,
GdkColorSpace *color_space,
GBytes *bytes,
gsize stride)
{
GdkMemoryTexture *self;
g_return_val_if_fail (width > 0, NULL);
g_return_val_if_fail (height > 0, NULL);
g_return_val_if_fail (GDK_IS_COLOR_SPACE (color_space), NULL);
g_return_val_if_fail (bytes != NULL, NULL);
g_return_val_if_fail (stride >= width * gdk_memory_format_bytes_per_pixel (format), NULL);
g_return_val_if_fail (gdk_color_space_supports_format (color_space, format), NULL);
bytes = gdk_memory_sanitize (bytes, width, height, format, stride, &stride);
self = g_object_new (GDK_TYPE_MEMORY_TEXTURE,
"width", width,
"height", height,
"color-space", color_space,
NULL);
GDK_TEXTURE (self)->format = format;
@@ -201,7 +249,8 @@ gdk_memory_texture_new_subtexture (GdkMemoryTexture *source,
GdkMemoryTexture *
gdk_memory_texture_from_texture (GdkTexture *texture,
GdkMemoryFormat format)
GdkMemoryFormat format,
GdkColorSpace *color_space)
{
GdkTexture *result;
GBytes *bytes;
@@ -214,20 +263,22 @@ gdk_memory_texture_from_texture (GdkTexture *texture,
{
GdkMemoryTexture *memtex = GDK_MEMORY_TEXTURE (texture);
if (gdk_texture_get_format (texture) == format)
if (gdk_texture_get_format (texture) == format &&
gdk_texture_get_color_space (texture) == color_space)
return g_object_ref (memtex);
}
stride = texture->width * gdk_memory_format_bytes_per_pixel (format);
data = g_malloc_n (stride, texture->height);
gdk_texture_do_download (texture, format, data, stride);
gdk_texture_do_download (texture, format, color_space, data, stride);
bytes = g_bytes_new_take (data, stride);
result = gdk_memory_texture_new (texture->width,
texture->height,
format,
bytes,
stride);
result = gdk_memory_texture_new_with_color_space (texture->width,
texture->height,
format,
color_space,
bytes,
stride);
g_bytes_unref (bytes);
return GDK_MEMORY_TEXTURE (result);

View File

@@ -26,6 +26,7 @@
#include <gdk/gdkenums.h>
#include <gdk/gdktexture.h>
#include <gdk/gdkcolorspace.h>
G_BEGIN_DECLS
@@ -68,6 +69,14 @@ GdkTexture * gdk_memory_texture_new (int
GdkMemoryFormat format,
GBytes *bytes,
gsize stride);
GDK_AVAILABLE_IN_4_10
GdkTexture * gdk_memory_texture_new_with_color_space
(int width,
int height,
GdkMemoryFormat format,
GdkColorSpace *color_space,
GBytes *bytes,
gsize stride);
G_END_DECLS

View File

@@ -30,7 +30,8 @@ G_BEGIN_DECLS
#define GDK_MEMORY_GDK_PIXBUF_ALPHA GDK_MEMORY_R8G8B8A8
GdkMemoryTexture * gdk_memory_texture_from_texture (GdkTexture *texture,
GdkMemoryFormat format);
GdkMemoryFormat format,
GdkColorSpace *color_space);
GdkTexture * gdk_memory_texture_new_subtexture (GdkMemoryTexture *texture,
int x,
int y,

View File

@@ -245,7 +245,8 @@ gdk_pixbuf_get_from_texture (GdkTexture *texture)
memtex = gdk_memory_texture_from_texture (texture,
alpha ? GDK_MEMORY_GDK_PIXBUF_ALPHA
: GDK_MEMORY_GDK_PIXBUF_OPAQUE);
: GDK_MEMORY_GDK_PIXBUF_OPAQUE,
gdk_color_space_get_srgb ());
return gdk_pixbuf_new_from_data (gdk_memory_texture_get_data (memtex),
GDK_COLORSPACE_RGB,

View File

@@ -30,6 +30,8 @@
#include "gdksurface.h"
#include "gdkprivate.h"
#include "gdkcolorspace.h"
#include "gdkcairo.h"
#include "gdkcontentprovider.h"
#include "gdkdeviceprivate.h"
#include "gdkdisplayprivate.h"
@@ -88,13 +90,14 @@ enum {
enum {
PROP_0,
PROP_COLOR_SPACE,
PROP_CURSOR,
PROP_DISPLAY,
PROP_FRAME_CLOCK,
PROP_MAPPED,
PROP_WIDTH,
PROP_HEIGHT,
PROP_MAPPED,
PROP_SCALE_FACTOR,
PROP_WIDTH,
LAST_PROP
};
@@ -485,6 +488,8 @@ gdk_surface_init (GdkSurface *surface)
surface->alpha = 255;
surface->color_space = g_object_ref (gdk_color_space_get_srgb ());
surface->device_cursor = g_hash_table_new_full (NULL, NULL,
NULL, g_object_unref);
}
@@ -500,6 +505,25 @@ gdk_surface_class_init (GdkSurfaceClass *klass)
klass->beep = gdk_surface_real_beep;
/**
* GdkSurface:color-space: (attributes org.gtk.Property.get=gdk_surface_get_color_space)
*
* The preferred color space for rendering to the surface
*
* This color space is negotiated between GTK and the compositor.
*
* The color space may change as the surface gets moved around - for example
* to different monitors or when the compositor gets reconfigured. As long as
* the surface isn't shown, the color space may not represent the actual color
* space that is going to be used.
*
* Since: 4.10
*/
properties[PROP_COLOR_SPACE] =
g_param_spec_object ("color-space", NULL, NULL,
GDK_TYPE_COLOR_SPACE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
/**
* GdkSurface:cursor: (attributes org.gtk.Property.get=gdk_surface_get_cursor org.gtk.Property.set=gdk_surface_set_cursor)
*
@@ -718,6 +742,7 @@ gdk_surface_finalize (GObject *object)
g_clear_object (&surface->cursor);
g_clear_pointer (&surface->device_cursor, g_hash_table_destroy);
g_clear_pointer (&surface->devices_inside, g_list_free);
g_clear_object (&surface->color_space);
g_clear_object (&surface->display);
@@ -772,6 +797,10 @@ gdk_surface_get_property (GObject *object,
switch (prop_id)
{
case PROP_COLOR_SPACE:
g_value_set_object (value, gdk_surface_get_color_space (surface));
break;
case PROP_CURSOR:
g_value_set_object (value, gdk_surface_get_cursor (surface));
break;
@@ -2040,6 +2069,40 @@ gdk_surface_get_height (GdkSurface *surface)
return surface->height;
}
void
gdk_surface_set_color_space (GdkSurface *self,
GdkColorSpace *color_space)
{
/* This way we support unsetting, too */
if (G_UNLIKELY (gdk_display_get_debug_flags (self->display) & GDK_DEBUG_SRGB))
color_space = gdk_color_space_get_srgb ();
if (gdk_color_space_equal (self->color_space, color_space))
return;
g_set_object (&self->color_space, color_space);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLOR_SPACE]);
}
/**
* gdk_surface_get_color_space: (attributes org.gtk.Method.get_property=color-space)
* @self: a `GdkSurface`
*
* Returns the preferred color space for rendering to the given @surface.
*
* Returns: (transfer none): The color space of @surface
*
* Since: 4.10
*/
GdkColorSpace *
gdk_surface_get_color_space (GdkSurface *self)
{
g_return_val_if_fail (GDK_IS_SURFACE (self), gdk_color_space_get_srgb ());
return self->color_space;
}
/*
* gdk_surface_get_origin:
* @surface: a `GdkSurface`
@@ -2360,7 +2423,7 @@ _gdk_windowing_got_event (GdkDisplay *display,
* with it.
*/
cairo_surface_t *
gdk_surface_create_similar_surface (GdkSurface * surface,
gdk_surface_create_similar_surface (GdkSurface * surface,
cairo_content_t content,
int width,
int height)
@@ -2376,6 +2439,7 @@ gdk_surface_create_similar_surface (GdkSurface * surface,
content == CAIRO_CONTENT_ALPHA ? CAIRO_FORMAT_A8 : CAIRO_FORMAT_ARGB32,
width * scale, height * scale);
cairo_surface_set_device_scale (similar_surface, scale, scale);
gdk_cairo_surface_set_color_space (similar_surface, gdk_surface_get_color_space (surface));
return similar_surface;
}

View File

@@ -86,17 +86,19 @@ GDK_AVAILABLE_IN_ALL
GdkCursor *gdk_surface_get_device_cursor (GdkSurface *surface,
GdkDevice *device);
GDK_AVAILABLE_IN_ALL
int gdk_surface_get_width (GdkSurface *surface);
int gdk_surface_get_width (GdkSurface *surface);
GDK_AVAILABLE_IN_ALL
int gdk_surface_get_height (GdkSurface *surface);
int gdk_surface_get_height (GdkSurface *surface);
GDK_AVAILABLE_IN_ALL
gboolean gdk_surface_translate_coordinates (GdkSurface *from,
GdkSurface *to,
double *x,
double *y);
int gdk_surface_get_scale_factor (GdkSurface *surface);
GDK_AVAILABLE_IN_4_10
GdkColorSpace * gdk_surface_get_color_space (GdkSurface *self);
GDK_AVAILABLE_IN_ALL
gboolean gdk_surface_translate_coordinates (GdkSurface *from,
GdkSurface *to,
double *x,
double *y);
GDK_AVAILABLE_IN_ALL
int gdk_surface_get_scale_factor (GdkSurface *surface);
GDK_AVAILABLE_IN_ALL
gboolean gdk_surface_get_device_position (GdkSurface *surface,

View File

@@ -52,6 +52,7 @@ struct _GdkSurface
int y;
GdkGLContext *gl_paint_context;
GdkColorSpace *color_space;
cairo_region_t *update_area;
guint update_freeze_count;
@@ -171,6 +172,8 @@ void gdk_surface_set_state (GdkSurface *surface,
void gdk_surface_set_is_mapped (GdkSurface *surface,
gboolean is_mapped);
void gdk_surface_set_color_space (GdkSurface *self,
GdkColorSpace *color_space);
GdkMonitor * gdk_surface_get_layout_monitor (GdkSurface *surface,
GdkPopupLayout *layout,

View File

@@ -40,6 +40,9 @@
#include "gdktextureprivate.h"
#include <glib/gi18n-lib.h>
#include "gdkcolorspace.h"
#include "gdkcairo.h"
#include "gdkmemoryformatprivate.h"
#include "gdkmemorytextureprivate.h"
#include "gdkpaintable.h"
#include "gdksnapshot.h"
@@ -61,6 +64,7 @@ enum {
PROP_0,
PROP_WIDTH,
PROP_HEIGHT,
PROP_COLOR_SPACE,
N_PROPS
};
@@ -223,6 +227,7 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GdkTexture, gdk_texture, G_TYPE_OBJECT,
static void
gdk_texture_default_download (GdkTexture *texture,
GdkMemoryFormat format,
GdkColorSpace *color_space,
guchar *data,
gsize stride)
{
@@ -247,6 +252,12 @@ gdk_texture_set_property (GObject *gobject,
self->height = g_value_get_int (value);
break;
case PROP_COLOR_SPACE:
self->color_space = g_value_dup_object (value);
if (self->color_space == NULL)
self->color_space = g_object_ref (gdk_color_space_get_srgb ());
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
@@ -271,6 +282,10 @@ gdk_texture_get_property (GObject *gobject,
g_value_set_int (value, self->height);
break;
case PROP_COLOR_SPACE:
g_value_set_object (value, self->color_space);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
@@ -283,6 +298,7 @@ gdk_texture_dispose (GObject *object)
GdkTexture *self = GDK_TEXTURE (object);
gdk_texture_clear_render_data (self);
g_clear_object (&self->color_space);
G_OBJECT_CLASS (gdk_texture_parent_class)->dispose (object);
}
@@ -305,9 +321,7 @@ gdk_texture_class_init (GdkTextureClass *klass)
*/
properties[PROP_WIDTH] =
g_param_spec_int ("width", NULL, NULL,
1,
G_MAXINT,
1,
1, G_MAXINT, 1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS |
@@ -320,14 +334,27 @@ gdk_texture_class_init (GdkTextureClass *klass)
*/
properties[PROP_HEIGHT] =
g_param_spec_int ("height", NULL, NULL,
1,
G_MAXINT,
1,
1, G_MAXINT, 1,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS |
G_PARAM_EXPLICIT_NOTIFY);
/**
* GdkTexture:color-space: (attributes org.gtk.Property.get=gdk_texture_get_color_space)
*
* The color space associated with texture.
*
* Since: 4.10
*/
properties[PROP_COLOR_SPACE] =
g_param_spec_object ("color-space", NULL, NULL,
GDK_TYPE_COLOR_SPACE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS |
G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
@@ -361,18 +388,43 @@ gdk_texture_new_for_surface (cairo_surface_t *surface)
* cairo_image_surface_get_stride (surface),
(GDestroyNotify) cairo_surface_destroy,
cairo_surface_reference (surface));
texture = gdk_memory_texture_new (cairo_image_surface_get_width (surface),
cairo_image_surface_get_height (surface),
GDK_MEMORY_DEFAULT,
bytes,
cairo_image_surface_get_stride (surface));
texture = gdk_memory_texture_new_with_color_space (cairo_image_surface_get_width (surface),
cairo_image_surface_get_height (surface),
GDK_MEMORY_DEFAULT,
gdk_cairo_surface_get_color_space (surface),
bytes,
cairo_image_surface_get_stride (surface));
g_bytes_unref (bytes);
return texture;
}
static GdkColorSpace *
gdk_color_space_from_pixbuf (GdkPixbuf *pixbuf)
{
const char *icc_profile_base64;
GdkColorSpace *color_space = NULL;
icc_profile_base64 = gdk_pixbuf_get_option (pixbuf, "icc-profile");
if (icc_profile_base64)
{
guchar *icc_data;
gsize icc_len;
GBytes *bytes;
icc_data = g_base64_decode (icc_profile_base64, &icc_len);
bytes = g_bytes_new_take (icc_data, icc_len);
color_space = gdk_color_space_new_from_icc_profile (bytes, NULL);
g_bytes_unref (bytes);
}
if (!color_space)
color_space = g_object_ref (gdk_color_space_get_srgb ());
return color_space;
}
/**
* gdk_texture_new_for_pixbuf:
* @pixbuf: a `GdkPixbuf`
@@ -390,24 +442,31 @@ gdk_texture_new_for_pixbuf (GdkPixbuf *pixbuf)
{
GdkTexture *texture;
GBytes *bytes;
GdkColorSpace *color_space;
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
color_space = gdk_color_space_from_pixbuf (pixbuf);
bytes = g_bytes_new_with_free_func (gdk_pixbuf_get_pixels (pixbuf),
gdk_pixbuf_get_height (pixbuf)
* gdk_pixbuf_get_rowstride (pixbuf),
g_object_unref,
g_object_ref (pixbuf));
texture = gdk_memory_texture_new (gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf),
gdk_pixbuf_get_has_alpha (pixbuf)
? GDK_MEMORY_GDK_PIXBUF_ALPHA
: GDK_MEMORY_GDK_PIXBUF_OPAQUE,
bytes,
gdk_pixbuf_get_rowstride (pixbuf));
texture = gdk_memory_texture_new_with_color_space (gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf),
gdk_pixbuf_get_has_alpha (pixbuf)
? GDK_MEMORY_GDK_PIXBUF_ALPHA
: GDK_MEMORY_GDK_PIXBUF_OPAQUE,
color_space,
bytes,
gdk_pixbuf_get_rowstride (pixbuf));
g_bytes_unref (bytes);
g_object_unref (color_space);
return texture;
}
@@ -665,32 +724,73 @@ gdk_texture_get_height (GdkTexture *texture)
return texture->height;
}
/**
* gdk_texture_get_color_space: (attributes org.gtk.Method.get_property=color-space)
* @texture: a `GdkTexture`
*
* Returns the color space associsated with @texture.
*
* Returns: (transfer none): the color space of the `GdkTexture`
*
* Since: 4.10
*/
GdkColorSpace *
gdk_texture_get_color_space (GdkTexture *texture)
{
g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
return texture->color_space;
}
void
gdk_texture_do_download (GdkTexture *texture,
GdkMemoryFormat format,
GdkColorSpace *color_space,
guchar *data,
gsize stride)
{
GDK_TEXTURE_GET_CLASS (texture)->download (texture, format, data,stride);
GDK_TEXTURE_GET_CLASS (texture)->download (texture, format, color_space, data, stride);
}
/*<private>
* gdk_texture_download_surface:
* @texture: the texture to download
* @color_space: (nullable): The target color space or %NULL for the default sRGB
*
* Downloads the texture into a newly created Cairo surface.
*
* Returns: A new Cairo surface.
**/
cairo_surface_t *
gdk_texture_download_surface (GdkTexture *texture)
gdk_texture_download_surface (GdkTexture *texture,
GdkColorSpace *color_space)
{
cairo_surface_t *surface;
cairo_status_t surface_status;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
texture->width, texture->height);
if (color_space != NULL)
gdk_cairo_surface_set_color_space (surface, color_space);
else
color_space = gdk_color_space_get_srgb ();
surface_status = cairo_surface_status (surface);
if (surface_status != CAIRO_STATUS_SUCCESS)
g_warning ("%s: surface error: %s", __FUNCTION__,
cairo_status_to_string (surface_status));
{
g_warning ("%s: surface error: %s", __FUNCTION__,
cairo_status_to_string (surface_status));
}
else
{
gdk_texture_do_download (texture,
GDK_MEMORY_DEFAULT,
color_space,
cairo_image_surface_get_data (surface),
cairo_image_surface_get_stride (surface));
}
gdk_texture_download (texture,
cairo_image_surface_get_data (surface),
cairo_image_surface_get_stride (surface));
cairo_surface_mark_dirty (surface);
return surface;
@@ -734,6 +834,7 @@ gdk_texture_download (GdkTexture *texture,
gdk_texture_do_download (texture,
GDK_MEMORY_DEFAULT,
gdk_color_space_get_srgb (),
data,
stride);
}
@@ -751,7 +852,7 @@ gdk_texture_set_render_data (GdkTexture *self,
GDestroyNotify notify)
{
g_return_val_if_fail (data != NULL, FALSE);
if (self->render_key != NULL)
return FALSE;

View File

@@ -82,6 +82,8 @@ GDK_AVAILABLE_IN_ALL
int gdk_texture_get_width (GdkTexture *texture) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
int gdk_texture_get_height (GdkTexture *texture) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_10
GdkColorSpace * gdk_texture_get_color_space (GdkTexture *texture) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
void gdk_texture_download (GdkTexture *texture,

View File

@@ -18,6 +18,7 @@ struct _GdkTexture
GdkMemoryFormat format;
int width;
int height;
GdkColorSpace *color_space;
gpointer render_key;
gpointer render_data;
@@ -30,6 +31,7 @@ struct _GdkTextureClass {
/* mandatory: Download in the given format into data */
void (* download) (GdkTexture *texture,
GdkMemoryFormat format,
GdkColorSpace *color_space,
guchar *data,
gsize stride);
};
@@ -37,10 +39,12 @@ struct _GdkTextureClass {
gboolean gdk_texture_can_load (GBytes *bytes);
GdkTexture * gdk_texture_new_for_surface (cairo_surface_t *surface);
cairo_surface_t * gdk_texture_download_surface (GdkTexture *texture);
cairo_surface_t * gdk_texture_download_surface (GdkTexture *texture,
GdkColorSpace *color_space);
void gdk_texture_do_download (GdkTexture *texture,
GdkMemoryFormat format,
GdkColorSpace *color_space,
guchar *data,
gsize stride);
GdkMemoryFormat gdk_texture_get_format (GdkTexture *self);

View File

@@ -73,6 +73,7 @@ typedef cairo_rectangle_int_t GdkRectangle;
/* Forward declarations of commonly used types */
typedef struct _GdkRGBA GdkRGBA;
typedef struct _GdkColorSpace GdkColorSpace;
typedef struct _GdkContentFormats GdkContentFormats;
typedef struct _GdkContentProvider GdkContentProvider;
typedef struct _GdkCursor GdkCursor;

View File

@@ -22,8 +22,9 @@
#include "gdkjpegprivate.h"
#include <glib/gi18n-lib.h>
#include "gdktexture.h"
#include "gdkcolorspace.h"
#include "gdkmemorytextureprivate.h"
#include "gdktexture.h"
#include "gdkprofilerprivate.h"
@@ -143,9 +144,12 @@ gdk_load_jpeg (GBytes *input_bytes,
guint width, height, stride;
unsigned char *data;
unsigned char *row[1];
JOCTET *icc_data;
unsigned int icc_len;
GBytes *bytes;
GdkTexture *texture;
GdkMemoryFormat format;
GdkColorSpace *color_space;
G_GNUC_UNUSED guint64 before = GDK_PROFILER_CURRENT_TIME;
info.err = jpeg_std_error (&jerr.pub);
@@ -167,6 +171,9 @@ gdk_load_jpeg (GBytes *input_bytes,
g_bytes_get_data (input_bytes, NULL),
g_bytes_get_size (input_bytes));
/* save color space */
jpeg_save_markers (&info, JPEG_APP0 + 2, 0xFFFF);
jpeg_read_header (&info, TRUE);
jpeg_start_decompress (&info);
@@ -224,19 +231,34 @@ gdk_load_jpeg (GBytes *input_bytes,
g_assert_not_reached ();
}
if (jpeg_read_icc_profile (&info, &icc_data, &icc_len))
{
GBytes *icc_bytes = g_bytes_new_with_free_func (icc_data, icc_len, free, icc_data);
color_space = gdk_color_space_new_from_icc_profile (icc_bytes, error);
g_bytes_unref (icc_bytes);
}
else
color_space = g_object_ref (gdk_color_space_get_srgb ());
jpeg_finish_decompress (&info);
jpeg_destroy_decompress (&info);
bytes = g_bytes_new_take (data, stride * height);
texture = gdk_memory_texture_new (width, height,
format,
bytes, stride);
if (color_space)
{
texture = gdk_memory_texture_new_with_color_space (width, height,
format,
color_space,
bytes, stride);
}
else
texture = NULL;
g_bytes_unref (bytes);
gdk_profiler_end_mark (before, "jpeg load", NULL);
return texture;
}
@@ -254,9 +276,12 @@ gdk_save_jpeg (GdkTexture *texture)
gsize texstride;
guchar *row;
int width, height;
GdkColorSpace *color_space;
GBytes *bytes;
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
color_space = gdk_texture_get_color_space (texture);
info.err = jpeg_std_error (&jerr.pub);
jerr.pub.error_exit = fatal_error_handler;
@@ -288,12 +313,22 @@ gdk_save_jpeg (GdkTexture *texture)
jpeg_mem_dest (&info, &data, &size);
memtex = gdk_memory_texture_from_texture (texture,
GDK_MEMORY_R8G8B8);
GDK_MEMORY_R8G8B8,
gdk_color_space_get_srgb ());
texdata = gdk_memory_texture_get_data (memtex);
texstride = gdk_memory_texture_get_stride (memtex);
jpeg_start_compress (&info, TRUE);
bytes = gdk_color_space_save_to_icc_profile (color_space, NULL);
if (bytes)
{
jpeg_write_icc_profile (&info,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes));
g_bytes_unref (bytes);
}
while (info.next_scanline < info.image_height)
{
row = (guchar *) texdata + info.next_scanline * texstride;

View File

@@ -20,12 +20,16 @@
#include "gdkpngprivate.h"
#include <glib/gi18n-lib.h>
#include "gdkcolorspaceprivate.h"
#include "gdklcmscolorspaceprivate.h"
#include "gdkmemoryformatprivate.h"
#include "gdkmemorytextureprivate.h"
#include "gdkprofilerprivate.h"
#include "gdktexture.h"
#include "gdktextureprivate.h"
#include "gsk/gl/fp16private.h"
#include <lcms2.h>
#include <png.h>
#include <stdio.h>
@@ -128,7 +132,106 @@ png_simple_warning_callback (png_structp png,
}
/* }}} */
/* {{{ Public API */
/* {{{ Color profile handling */
static GdkColorSpace *
gdk_png_get_color_space (png_struct *png,
png_info *info)
{
GdkColorSpace *color_space;
guchar *icc_data;
png_uint_32 icc_len;
char *name;
double gamma;
cmsCIExyY whitepoint;
cmsCIExyYTRIPLE primaries;
cmsToneCurve *curve;
cmsHPROFILE lcms_profile;
int intent;
if (png_get_iCCP (png, info, &name, NULL, &icc_data, &icc_len))
{
GBytes *bytes = g_bytes_new (icc_data, icc_len);
color_space = gdk_color_space_new_from_icc_profile (bytes, NULL);
g_bytes_unref (bytes);
if (color_space)
return color_space;
}
if (png_get_sRGB (png, info, &intent))
return g_object_ref (gdk_color_space_get_srgb ());
/* If neither of those is valid, the result is sRGB */
if (!png_get_valid (png, info, PNG_INFO_gAMA) &&
!png_get_valid (png, info, PNG_INFO_cHRM))
return g_object_ref (gdk_color_space_get_srgb ());
if (!png_get_gAMA (png, info, &gamma))
gamma = 2.4;
if (!png_get_cHRM (png, info,
&whitepoint.x, &whitepoint.y,
&primaries.Red.x, &primaries.Red.y,
&primaries.Green.x, &primaries.Green.y,
&primaries.Blue.x, &primaries.Blue.y))
{
if (gamma == 2.4)
return g_object_ref (gdk_color_space_get_srgb ());
whitepoint = (cmsCIExyY) { 0.3127, 0.3290, 1.0 };
primaries = (cmsCIExyYTRIPLE) {
{ 0.6400, 0.3300, 1.0 },
{ 0.3000, 0.6000, 1.0 },
{ 0.1500, 0.0600, 1.0 }
};
}
else
{
primaries.Red.Y = 1.0;
primaries.Green.Y = 1.0;
primaries.Blue.Y = 1.0;
}
curve = cmsBuildGamma (NULL, 1.0 / gamma);
lcms_profile = cmsCreateRGBProfile (&whitepoint,
&primaries,
(cmsToneCurve*[3]) { curve, curve, curve });
color_space = gdk_lcms_color_space_new_from_lcms_profile (lcms_profile);
/* FIXME: errors? */
if (color_space == NULL)
color_space = g_object_ref (gdk_color_space_get_srgb ());
cmsFreeToneCurve (curve);
return color_space;
}
static void
gdk_png_set_color_space (png_struct *png,
png_info *info,
GdkColorSpace *color_space)
{
/* FIXME: allow deconstructing RGB profiles into gAMA and cHRM
* instead of falling back to iCCP
*/
if (color_space == gdk_color_space_get_srgb ())
{
png_set_sRGB_gAMA_and_cHRM (png, info, /* FIXME */ PNG_sRGB_INTENT_PERCEPTUAL);
}
else
{
GBytes *bytes = gdk_color_space_save_to_icc_profile (color_space, NULL);
png_set_iCCP (png, info,
"ICC profile",
0,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes));
g_bytes_unref (bytes);
}
}
/* }}} */
/* {{{ Public API */
GdkTexture *
gdk_load_png (GBytes *bytes,
@@ -144,6 +247,7 @@ gdk_load_png (GBytes *bytes,
guchar *buffer = NULL;
guchar **row_pointers = NULL;
GBytes *out_bytes;
GdkColorSpace *color_space;
GdkTexture *texture;
int bpp;
G_GNUC_UNUSED gint64 before = GDK_PROFILER_CURRENT_TIME;
@@ -248,6 +352,8 @@ gdk_load_png (GBytes *bytes,
return NULL;
}
color_space = gdk_png_get_color_space (png, info);
bpp = gdk_memory_format_bytes_per_pixel (format);
stride = width * bpp;
if (stride % 8)
@@ -258,6 +364,7 @@ gdk_load_png (GBytes *bytes,
if (!buffer || !row_pointers)
{
g_object_unref (color_space);
g_free (buffer);
g_free (row_pointers);
png_destroy_read_struct (&png, &info, NULL);
@@ -274,8 +381,11 @@ gdk_load_png (GBytes *bytes,
png_read_end (png, info);
out_bytes = g_bytes_new_take (buffer, height * stride);
texture = gdk_memory_texture_new (width, height, format, out_bytes, stride);
texture = gdk_memory_texture_new_with_color_space (width, height,
format, color_space,
out_bytes, stride);
g_bytes_unref (out_bytes);
g_object_unref (color_space);
g_free (row_pointers);
png_destroy_read_struct (&png, &info, NULL);
@@ -302,13 +412,17 @@ gdk_save_png (GdkTexture *texture)
int y;
GdkMemoryTexture *memtex;
GdkMemoryFormat format;
GdkColorSpace *color_space;
int png_format;
int depth;
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
color_space = gdk_texture_get_color_space (texture);
format = gdk_texture_get_format (texture);
memtex = gdk_memory_texture_from_texture (texture, format, color_space);
switch (format)
{
case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
@@ -318,14 +432,22 @@ gdk_save_png (GdkTexture *texture)
case GDK_MEMORY_A8R8G8B8:
case GDK_MEMORY_R8G8B8A8:
case GDK_MEMORY_A8B8G8R8:
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
format = GDK_MEMORY_R8G8B8A8;
#elif G_BYTE_ORDER == G_BIG_ENDIAN
format = GDK_MEMORY_A8B8G8R8;
#endif
png_format = PNG_COLOR_TYPE_RGB_ALPHA;
depth = 8;
break;
case GDK_MEMORY_R8G8B8:
case GDK_MEMORY_B8G8R8:
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
format = GDK_MEMORY_R8G8B8;
#elif G_BYTE_ORDER == G_BIG_ENDIAN
format = GDK_MEMORY_B8G8R8;
#endif
png_format = PNG_COLOR_TYPE_RGB;
depth = 8;
break;
@@ -370,7 +492,7 @@ gdk_save_png (GdkTexture *texture)
return NULL;
}
memtex = gdk_memory_texture_from_texture (texture, format);
memtex = gdk_memory_texture_from_texture (texture, format, gdk_color_space_get_srgb ());
if (sigsetjmp (png_jmpbuf (png), 1))
{
@@ -388,6 +510,8 @@ gdk_save_png (GdkTexture *texture)
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
gdk_png_set_color_space (png, info, color_space);
png_write_info (png, info);
#if G_BYTE_ORDER == G_LITTLE_ENDIAN

View File

@@ -220,6 +220,44 @@ tiff_open_write (GBytes **result)
NULL, NULL);
}
/* }}} */
/* {{{ Color profile handling */
static GdkColorSpace *
gdk_tiff_get_color_space (TIFF *tiff)
{
const char *icc_data;
guint icc_len;
if (TIFFGetField (tiff, TIFFTAG_ICCPROFILE, &icc_len, &icc_data))
{
GBytes *icc_bytes;
GdkColorSpace *color_space;
icc_bytes = g_bytes_new (icc_data, icc_len);
color_space = gdk_color_space_new_from_icc_profile (icc_bytes, NULL);
g_bytes_unref (icc_bytes);
if (color_space)
return color_space;
}
return g_object_ref (gdk_color_space_get_srgb ());
}
static void
gdk_tiff_set_color_space (TIFF *tiff,
GdkColorSpace *color_space)
{
GBytes *bytes = gdk_color_space_save_to_icc_profile (color_space, NULL);
TIFFSetField (tiff, TIFFTAG_ICCPROFILE,
g_bytes_get_size (bytes),
g_bytes_get_data (bytes, NULL));
g_bytes_unref (bytes);
}
/* }}} */
/* {{{ Public API */
@@ -266,6 +304,7 @@ gdk_save_tiff (GdkTexture *texture)
GBytes *result = NULL;
GdkMemoryTexture *memtex;
GdkMemoryFormat format;
GdkColorSpace *color_space;
const FormatData *fdata = NULL;
tif = tiff_open_write (&result);
@@ -273,6 +312,7 @@ gdk_save_tiff (GdkTexture *texture)
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
format = gdk_texture_get_format (texture);
color_space = gdk_texture_get_color_space (texture);
fdata = &format_data[format];
if (fdata == NULL)
@@ -292,7 +332,9 @@ gdk_save_tiff (GdkTexture *texture)
TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
memtex = gdk_memory_texture_from_texture (texture, fdata->format);
gdk_tiff_set_color_space (tif, color_space);
memtex = gdk_memory_texture_from_texture (texture, fdata->format, color_space);
data = gdk_memory_texture_get_data (memtex);
stride = gdk_memory_texture_get_stride (memtex);
@@ -326,6 +368,7 @@ load_fallback (TIFF *tif,
int width, height;
guchar *data;
GBytes *bytes;
GdkColorSpace *color_space;
GdkTexture *texture;
TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &width);
@@ -342,12 +385,15 @@ load_fallback (TIFF *tif,
return NULL;
}
color_space = gdk_tiff_get_color_space (tif);
bytes = g_bytes_new_take (data, width * height * 4);
texture = gdk_memory_texture_new (width, height,
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
bytes,
width * 4);
texture = gdk_memory_texture_new_with_color_space (width, height,
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
color_space,
bytes,
width * 4);
g_bytes_unref (bytes);
@@ -372,6 +418,7 @@ gdk_load_tiff (GBytes *input_bytes,
gsize stride;
int bpp;
GBytes *bytes;
GdkColorSpace *color_space;
GdkTexture *texture;
G_GNUC_UNUSED gint64 before = GDK_PROFILER_CURRENT_TIME;
@@ -471,12 +518,14 @@ gdk_load_tiff (GBytes *input_bytes,
line += stride;
}
color_space = gdk_tiff_get_color_space (tif);
bpp = gdk_memory_format_bytes_per_pixel (format);
bytes = g_bytes_new_take (data, width * height * bpp);
texture = gdk_memory_texture_new (width, height,
format,
bytes, width * bpp);
texture = gdk_memory_texture_new_with_color_space (width, height,
format, color_space,
bytes, width * bpp);
g_bytes_unref (bytes);
TIFFClose (tif);

View File

@@ -3,7 +3,10 @@ gdk_public_sources = files([
'gdkapplaunchcontext.c',
'gdkcairo.c',
'gdkcairocontext.c',
'gdkcicpcolorspace.c',
'gdkclipboard.c',
'gdkcolorspace.c',
'gdkcolor.c',
'gdkcontentdeserializer.c',
'gdkcontentformats.c',
'gdkcontentprovider.c',
@@ -30,6 +33,7 @@ gdk_public_sources = files([
'gdkhsla.c',
'gdkkeys.c',
'gdkkeyuni.c',
'gdklcmscolorspace.c',
'gdkmemoryformat.c',
'gdkmemorytexture.c',
'gdkmonitor.c',
@@ -63,6 +67,7 @@ gdk_public_headers = files([
'gdkcairo.h',
'gdkcairocontext.h',
'gdkclipboard.h',
'gdkcolorspace.h',
'gdkcontentdeserializer.h',
'gdkcontentformats.h',
'gdkcontentprovider.h',
@@ -205,6 +210,7 @@ gdk_deps = [
fontconfig_dep,
platform_gio_dep,
pangocairo_dep,
lcms2_dep,
vulkan_dep,
png_dep,
tiff_dep,

View File

@@ -157,6 +157,9 @@ gdk_wayland_cairo_context_begin_frame (GdkDrawContext *draw_context,
else
self->paint_surface = gdk_wayland_cairo_context_create_surface (self);
gdk_cairo_surface_set_color_space (self->paint_surface,
gdk_surface_get_color_space (gdk_draw_context_get_surface (draw_context)));
surface_region = gdk_wayland_cairo_context_surface_get_region (self->paint_surface);
if (surface_region)
cairo_region_union (region, surface_region);

View File

@@ -47,6 +47,7 @@ create_cairo_surface_for_surface (GdkSurface *surface,
cairo_surface = cairo_win32_surface_create_with_format (hdc, CAIRO_FORMAT_ARGB32);
cairo_surface_set_device_scale (cairo_surface, scale, scale);
gdk_cairo_surface_set_color_space (cairo_surface, gdk_surface_get_color_space (surface));
return cairo_surface;
}

View File

@@ -49,6 +49,7 @@ create_cairo_surface_for_surface (GdkSurface *surface)
gdk_surface_get_width (surface) * scale,
gdk_surface_get_height (surface) * scale);
cairo_surface_set_device_scale (cairo_surface, scale, scale);
gdk_cairo_surface_set_color_space (cairo_surface, gdk_surface_get_color_space(surface));
return cairo_surface;
}

View File

@@ -1402,6 +1402,71 @@ gdk_x11_display_init_leader_surface (GdkX11Display *self)
self->leader_window_title_set = FALSE;
}
static void
voidXFree (gpointer data)
{
XFree (data);
}
static void
gdk_x11_display_check_color_space (GdkX11Display *self)
{
GdkDisplay *display = GDK_DISPLAY (self);
GdkX11Screen *screen;
char *atom_name;
Atom type;
int result;
int format;
gulong nitems;
gulong bytes_after;
guchar *data;
GBytes *bytes;
screen = self->screen;
if (screen->screen_num > 0)
atom_name = g_strdup_printf ("_ICC_PROFILE_%d", screen->screen_num);
else
atom_name = g_strdup ("_ICC_PROFILE");
g_clear_object (&self->color_space);
self->color_space = g_object_ref (gdk_color_space_get_srgb ());
gdk_x11_display_error_trap_push (display);
result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
screen->xroot_window,
gdk_x11_get_xatom_by_name_for_display (display, atom_name),
0, G_MAXLONG, False, XA_CARDINAL, &type,
&format, &nitems,
&bytes_after, &data);
gdk_x11_display_error_trap_pop_ignored (display);
g_free (atom_name);
if (result != Success || type != XA_CARDINAL || nitems <= 0)
return;
switch (format)
{
case 8:
bytes = g_bytes_new_with_free_func (data, nitems, voidXFree, data);
break;
case 16:
bytes = g_bytes_new_with_free_func (data, sizeof (short) * nitems, voidXFree, data);
break;
case 32:
bytes = g_bytes_new_with_free_func (data, sizeof (long) * nitems, voidXFree, data);
break;
default:
XFree (data);
return;
}
g_clear_object (&self->color_space);
self->color_space = gdk_color_space_new_from_icc_profile (bytes, NULL);
if (!self->color_space)
self->color_space = g_object_ref (gdk_color_space_get_srgb ());
}
/**
* gdk_x11_display_open:
* @display_name: (nullable): name of the X display.
@@ -1469,6 +1534,9 @@ gdk_x11_display_open (const char *display_name)
/* initialize the display's screens */
display_x11->screen = _gdk_x11_screen_new (display, DefaultScreen (display_x11->xdisplay));
/* We want this for the leader surface already */
gdk_x11_display_check_color_space (display_x11);
/* If GL is available we want to pick better default/rgba visuals,
* as we care about GLX details such as alpha/depth/stencil depth,
* stereo and double buffering
@@ -1915,15 +1983,17 @@ gdk_x11_display_ungrab (GdkDisplay *display)
static void
gdk_x11_display_dispose (GObject *object)
{
GdkX11Display *display_x11 = GDK_X11_DISPLAY (object);
GdkX11Display *self = GDK_X11_DISPLAY (object);
if (display_x11->event_source)
if (self->event_source)
{
g_source_destroy (display_x11->event_source);
g_source_unref (display_x11->event_source);
display_x11->event_source = NULL;
g_source_destroy (self->event_source);
g_source_unref (self->event_source);
self->event_source = NULL;
}
g_clear_object (&self->color_space);
G_OBJECT_CLASS (gdk_x11_display_parent_class)->dispose (object);
}

View File

@@ -128,6 +128,9 @@ struct _GdkX11Display
guint have_damage;
#endif
/* Stored in the ICC_PROFILE rootwindow prop */
GdkColorSpace *color_space;
/* If GL is not supported, store the error here */
GError *gl_error;

View File

@@ -1011,6 +1011,8 @@ setup_toplevel_window (GdkSurface *surface,
/* This will set WM_CLIENT_MACHINE and WM_LOCALE_NAME */
XSetWMProperties (xdisplay, xid, NULL, NULL, NULL, 0, NULL, NULL, NULL);
gdk_surface_set_color_space (surface, GDK_X11_DISPLAY (display)->color_space);
if (!gdk_running_in_sandbox ())
{
@@ -3185,7 +3187,7 @@ gdk_surface_update_icon (GdkSurface *surface,
toplevel->icon_pixmap = gdk_x11_surface_create_pixmap_surface (surface, width, height);
cairo_surface = gdk_texture_download_surface (best_icon);
cairo_surface = gdk_texture_download_surface (best_icon, NULL);
cr = cairo_create (toplevel->icon_pixmap);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);

View File

@@ -449,7 +449,7 @@ get_colorized_texture (GdkTexture *texture,
const graphene_matrix_t *color_matrix,
const graphene_vec4_t *color_offset)
{
cairo_surface_t *surface = gdk_texture_download_surface (texture);
cairo_surface_t *surface = gdk_texture_download_surface (texture, NULL);
cairo_surface_t *image_surface;
graphene_vec4_t pixel;
guint32* pixel_data;

View File

@@ -28,6 +28,7 @@
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdkmemoryformatprivate.h>
#include <gdk/gdkmemorytextureprivate.h>
#include <gdk/gdkcolorspaceprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include <gsk/gskdebugprivate.h>
#include <gsk/gskroundedrectprivate.h>
@@ -529,7 +530,7 @@ gsk_gl_command_queue_begin_draw (GskGLCommandQueue *self,
GskGLCommandBatch *batch;
g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
g_assert (self->in_draw == FALSE);
g_assert (!self->in_draw);
g_assert (width <= G_MAXUINT16);
g_assert (height <= G_MAXUINT16);
@@ -567,6 +568,7 @@ gsk_gl_command_queue_end_draw (GskGLCommandQueue *self)
GskGLCommandBatch *batch;
g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
g_assert (self->in_draw);
g_assert (self->batches.len > 0);
if (will_ignore_batch (self))
@@ -1351,23 +1353,35 @@ gsk_gl_command_queue_create_framebuffer (GskGLCommandQueue *self)
static void
gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
GdkTexture *texture)
GdkTexture *texture,
GskConversion *conversion)
{
GdkGLContext *context;
const guchar *data;
gsize stride;
GdkMemoryTexture *memtex;
GdkMemoryFormat data_format;
GdkColorSpace *data_space;
int width, height;
GLenum gl_internalformat;
GLenum gl_format;
GLenum gl_type;
gsize bpp;
gboolean use_es;
gboolean convert_locally = FALSE;
context = gdk_gl_context_get_current ();
use_es = gdk_gl_context_get_use_es (context);
data_format = gdk_texture_get_format (texture);
data_space = gdk_texture_get_color_space (texture);
if (data_space == gdk_color_space_get_srgb ())
*conversion = GSK_CONVERSION_LINEARIZE;
else if (data_space == gdk_color_space_get_srgb_linear ())
*conversion = 0;
else /* FIXME: do colorspace conversion in a shader */
convert_locally = TRUE;
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
@@ -1377,21 +1391,38 @@ gsk_gl_command_queue_do_upload_texture (GskGLCommandQueue *self,
&gl_format,
&gl_type))
{
if (gdk_memory_format_prefers_high_depth (data_format))
data_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
else
data_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
if (!gdk_memory_format_gl_format (data_format,
use_es,
&gl_internalformat,
&gl_format,
&gl_type))
if (gdk_gl_context_get_api (context) == GDK_GL_API_GL)
{
g_assert_not_reached ();
*conversion |= GSK_CONVERSION_PREMULTIPLY;
}
else
{
convert_locally = TRUE;
if (gdk_memory_format_prefers_high_depth (data_format))
data_format = GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED;
else
data_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
if (!gdk_memory_format_gl_format (data_format,
use_es,
&gl_internalformat,
&gl_format,
&gl_type))
{
g_assert_not_reached ();
}
}
}
memtex = gdk_memory_texture_from_texture (texture, data_format);
if (convert_locally)
{
memtex = gdk_memory_texture_from_texture (texture, data_format, gdk_color_space_get_srgb_linear ());
*conversion = 0;
}
else
{
memtex = gdk_memory_texture_from_texture (texture, gdk_texture_get_format (texture), gdk_texture_get_color_space (texture));
}
data = gdk_memory_texture_get_data (memtex);
stride = gdk_memory_texture_get_stride (memtex);
bpp = gdk_memory_format_bytes_per_pixel (data_format);
@@ -1430,11 +1461,13 @@ int
gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
GdkTexture *texture,
int min_filter,
int mag_filter)
int mag_filter,
GskConversion *conversion)
{
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
cairo_surface_t *surface = NULL;
int width, height;
int format;
int texture_id;
g_assert (GSK_IS_GL_COMMAND_QUEUE (self));
@@ -1444,6 +1477,9 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
*conversion = 0;
if (width > self->max_texture_size || height > self->max_texture_size)
{
g_warning ("Attempt to create texture of size %ux%u but max size is %d. "
@@ -1452,7 +1488,9 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
width = MAX (width, self->max_texture_size);
height = MAX (height, self->max_texture_size);
}
texture_id = gsk_gl_command_queue_create_texture (self, width, height, GL_RGBA8, min_filter, mag_filter);
format = gdk_memory_format_prefers_high_depth (gdk_texture_get_format (texture)) ? GL_RGBA16F : GL_RGBA8;
texture_id = gsk_gl_command_queue_create_texture (self, width, height, format, min_filter, mag_filter);
if (texture_id == -1)
return texture_id;
@@ -1462,7 +1500,7 @@ gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, texture_id);
gsk_gl_command_queue_do_upload_texture (self, texture);
gsk_gl_command_queue_do_upload_texture (self, texture, conversion);
/* Restore previous texture state if any */
if (self->attachments->textures[0].id > 0)

View File

@@ -280,10 +280,18 @@ void gsk_gl_command_queue_execute (GskGLCommandQueue
guint scale_factor,
const cairo_region_t *scissor,
guint default_framebuffer);
typedef enum {
GSK_CONVERSION_LINEARIZE = 1 << 0,
GSK_CONVERSION_PREMULTIPLY = 1 << 1,
GSK_CONVERSION_FLIP = 1 << 2,
} GskConversion;
int gsk_gl_command_queue_upload_texture (GskGLCommandQueue *self,
GdkTexture *texture,
int min_filter,
int mag_filter);
int mag_filter,
GskConversion *remaining);
int gsk_gl_command_queue_create_texture (GskGLCommandQueue *self,
int width,
int height,

View File

@@ -40,7 +40,9 @@
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdkdisplayprivate.h>
#include <gdk/gdkmemoryformatprivate.h>
#include <gdk/gdkmemorytextureprivate.h>
#include <gdk/gdkcolorspaceprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdktextureprivate.h>
@@ -689,6 +691,138 @@ gsk_gl_driver_cache_texture (GskGLDriver *self,
g_hash_table_insert (self->texture_id_to_key, GUINT_TO_POINTER (texture_id), k);
}
static void
draw_rect (GskGLCommandQueue *command_queue,
float min_x,
float min_y,
float max_x,
float max_y,
gboolean flip)
{
GskGLDrawVertex *vertices = gsk_gl_command_queue_add_vertices (command_queue);
float min_u = 0;
float max_u = 1;
float min_v = flip ? 0 : 1;
float max_v = flip ? 1 : 0;
guint16 c = FP16_ZERO;
vertices[0] = (GskGLDrawVertex) { .position = { min_x, min_y }, .uv = { min_u, min_v }, .color = { c, c, c, c } };
vertices[1] = (GskGLDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c, c, c, c } };
vertices[2] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c, c, c, c } };
vertices[3] = (GskGLDrawVertex) { .position = { max_x, max_y }, .uv = { max_u, max_v }, .color = { c, c, c, c } };
vertices[4] = (GskGLDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c, c, c, c } };
vertices[5] = (GskGLDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c, c, c, c } };
}
static void
set_viewport_for_size (GskGLDriver *self,
GskGLProgram *program,
float width,
float height)
{
float viewport[4] = { 0, 0, width, height };
gsk_gl_uniform_state_set4fv (program->uniforms,
program->program_info,
UNIFORM_SHARED_VIEWPORT, 0,
1,
(const float *)&viewport);
self->stamps[UNIFORM_SHARED_VIEWPORT]++;
}
#define ORTHO_NEAR_PLANE -10000
#define ORTHO_FAR_PLANE 10000
static void
set_projection_for_size (GskGLDriver *self,
GskGLProgram *program,
float width,
float height)
{
graphene_matrix_t projection;
graphene_matrix_init_ortho (&projection, 0, width, 0, height, ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE);
graphene_matrix_scale (&projection, 1, -1, 1);
gsk_gl_uniform_state_set_matrix (program->uniforms,
program->program_info,
UNIFORM_SHARED_PROJECTION, 0,
&projection);
self->stamps[UNIFORM_SHARED_PROJECTION]++;
}
static void
reset_modelview (GskGLDriver *self,
GskGLProgram *program)
{
graphene_matrix_t modelview;
graphene_matrix_init_identity (&modelview);
gsk_gl_uniform_state_set_matrix (program->uniforms,
program->program_info,
UNIFORM_SHARED_MODELVIEW, 0,
&modelview);
self->stamps[UNIFORM_SHARED_MODELVIEW]++;
}
static GskGLTexture *
gsk_gl_driver_convert_texture (GskGLDriver *self,
int texture_id,
int width,
int height,
int format,
int min_filter,
int max_filter,
GskConversion conversion)
{
GskGLRenderTarget *target;
int prev_fbo;
GskGLProgram *program;
if ((conversion & (GSK_CONVERSION_LINEARIZE | GSK_CONVERSION_PREMULTIPLY)) == (GSK_CONVERSION_LINEARIZE | GSK_CONVERSION_PREMULTIPLY))
program = self->linearize_premultiply_no_clip;
else if (conversion & GSK_CONVERSION_LINEARIZE)
program = self->linearize_no_clip;
else if (conversion & GSK_CONVERSION_PREMULTIPLY)
program = self->premultiply_no_clip;
else
g_assert_not_reached ();
gdk_gl_context_make_current (self->command_queue->context);
gsk_gl_driver_create_render_target (self,
width, height,
format,
min_filter, max_filter,
&target);
prev_fbo = gsk_gl_command_queue_bind_framebuffer (self->command_queue, target->framebuffer_id);
gsk_gl_command_queue_clear (self->command_queue, 0, &GRAPHENE_RECT_INIT (0, 0, width, height));
gsk_gl_command_queue_begin_draw (self->command_queue,
program->program_info,
width, height);
set_projection_for_size (self, program, width, height);
set_viewport_for_size (self, program, width, height);
reset_modelview (self, program);
gsk_gl_program_set_uniform_texture (program,
UNIFORM_SHARED_SOURCE, 0,
GL_TEXTURE_2D, GL_TEXTURE0, texture_id);
draw_rect (self->command_queue, 0, 0, width, height, (conversion & GSK_CONVERSION_FLIP) != 0);
gsk_gl_command_queue_end_draw (self->command_queue);
gsk_gl_command_queue_bind_framebuffer (self->command_queue, prev_fbo);
texture_id = gsk_gl_driver_release_render_target (self, target, FALSE);
return g_hash_table_lookup (self->textures, GUINT_TO_POINTER (texture_id));
}
/**
* gsk_gl_driver_load_texture:
* @self: a `GdkTexture`
@@ -719,20 +853,29 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
int mag_filter)
{
GdkGLContext *context;
GdkMemoryTexture *downloaded_texture;
GdkMemoryTexture *downloaded_texture = NULL;
guint texture_id = 0;
GskGLTexture *t;
guint texture_id;
int height;
int width;
int format;
GskConversion conversion;
g_return_val_if_fail (GSK_IS_GL_DRIVER (self), 0);
g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
g_return_val_if_fail (GSK_IS_GL_COMMAND_QUEUE (self->command_queue), 0);
context = self->command_queue->context;
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
format = GL_RGBA8;
format = gdk_memory_format_prefers_high_depth (gdk_texture_get_format (texture)) ? GL_RGBA16F : GL_RGBA8;
if ((t = gdk_texture_get_render_data (texture, self)))
{
if (t->min_filter == min_filter && t->mag_filter == mag_filter)
return t->texture_id;
}
if (GDK_IS_GL_TEXTURE (texture))
{
@@ -741,35 +884,62 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
if (gdk_gl_context_is_shared (context, texture_context))
{
/* A GL texture from the same GL context is a simple task... */
return gdk_gl_texture_get_id (gl_texture);
}
else
{
downloaded_texture = gdk_memory_texture_from_texture (texture, gdk_texture_get_format (texture));
}
}
else
{
if ((t = gdk_texture_get_render_data (texture, self)))
{
if (t->min_filter == min_filter && t->mag_filter == mag_filter)
return t->texture_id;
}
GdkColorSpace *color_space;
guint gl_texture_id;
GdkGLTextureFlags flags;
downloaded_texture = gdk_memory_texture_from_texture (texture, gdk_texture_get_format (texture));
gl_texture_id = gdk_gl_texture_get_id (gl_texture);
color_space = gdk_texture_get_color_space (texture);
flags = gdk_gl_texture_get_flags (gl_texture);
/* A GL texture from the same GL context is a simple task... */
if (color_space == gdk_color_space_get_srgb_linear () &&
flags == GDK_GL_TEXTURE_PREMULTIPLIED)
{
return gl_texture_id;
}
else if (color_space == gdk_color_space_get_srgb () ||
color_space == gdk_color_space_get_srgb_linear ())
{
conversion = 0;
if (color_space == gdk_color_space_get_srgb ())
conversion |= GSK_CONVERSION_LINEARIZE;
if ((flags & GDK_GL_TEXTURE_PREMULTIPLIED) == 0)
conversion |= GSK_CONVERSION_PREMULTIPLY;
if ((flags & GDK_GL_TEXTURE_FLIPPED) != 0)
conversion |= GSK_CONVERSION_FLIP;
t = gsk_gl_driver_convert_texture (self,
gl_texture_id,
width, height,
format,
min_filter, mag_filter,
conversion);
if (gdk_texture_set_render_data (texture, self, t, gsk_gl_texture_destroyed))
t->user = texture;
return t->texture_id;
}
/* FIXME: do colorspace conversion in a shader */
}
}
downloaded_texture = gdk_memory_texture_from_texture (texture,
gdk_texture_get_format (texture),
gdk_texture_get_color_space (texture));
/* The download_texture() call may have switched the GL context. Make sure
* the right context is at work again. */
* the right context is at work again.
*/
gdk_gl_context_make_current (context);
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
texture_id = gsk_gl_command_queue_upload_texture (self->command_queue,
GDK_TEXTURE (downloaded_texture),
min_filter,
mag_filter);
min_filter, mag_filter,
&conversion);
t = gsk_gl_texture_new (texture_id,
width, height, format, min_filter, mag_filter,
@@ -777,6 +947,15 @@ gsk_gl_driver_load_texture (GskGLDriver *self,
g_hash_table_insert (self->textures, GUINT_TO_POINTER (texture_id), t);
g_clear_object (&downloaded_texture);
if (conversion)
t = gsk_gl_driver_convert_texture (self,
texture_id,
width, height, format,
min_filter, mag_filter,
conversion);
if (gdk_texture_set_render_data (texture, self, t, gsk_gl_texture_destroyed))
t->user = texture;
@@ -835,6 +1014,8 @@ gsk_gl_driver_create_texture (GskGLDriver *self,
GUINT_TO_POINTER (texture->texture_id),
texture);
texture->last_used_in_frame = self->current_frame_id;
return texture;
}
@@ -1202,6 +1383,7 @@ gsk_gl_driver_add_texture_slices (GskGLDriver *self,
GskGLTextureSlice *slices;
GskGLTexture *t;
guint n_slices;
int format;
guint cols;
guint rows;
int tex_width;
@@ -1219,6 +1401,9 @@ gsk_gl_driver_add_texture_slices (GskGLDriver *self,
tex_width = texture->width;
tex_height = texture->height;
format = gdk_memory_format_prefers_high_depth (gdk_texture_get_format (texture)) ? GL_RGBA16F : GL_RGBA8;
cols = (texture->width / max_texture_size) + 1;
rows = (texture->height / max_texture_size) + 1;
@@ -1232,7 +1417,8 @@ gsk_gl_driver_add_texture_slices (GskGLDriver *self,
n_slices = cols * rows;
slices = g_new0 (GskGLTextureSlice, n_slices);
memtex = gdk_memory_texture_from_texture (texture,
gdk_texture_get_format (texture));
gdk_texture_get_format (texture),
gdk_texture_get_color_space (texture));
for (guint col = 0; col < cols; col ++)
{
@@ -1244,13 +1430,38 @@ gsk_gl_driver_add_texture_slices (GskGLDriver *self,
int slice_index = (col * rows) + row;
GdkTexture *subtex;
guint texture_id;
GskConversion conversion;
subtex = gdk_memory_texture_new_subtexture (memtex,
x, y,
slice_width, slice_height);
texture_id = gsk_gl_command_queue_upload_texture (self->command_queue,
subtex,
GL_NEAREST, GL_NEAREST);
GL_NEAREST, GL_NEAREST,
&conversion);
if (conversion)
{
t = gsk_gl_texture_new (texture_id,
slice_width, slice_height,
format,
GL_NEAREST, GL_NEAREST,
0);
g_hash_table_insert (self->textures, GUINT_TO_POINTER (texture_id), t);
t = gsk_gl_driver_convert_texture (self,
texture_id,
slice_width, slice_height,
format,
GL_NEAREST, GL_NEAREST,
conversion);
texture_id = t->texture_id;
t->texture_id = 0;
g_hash_table_steal (self->textures, GUINT_TO_POINTER (texture_id));
gsk_gl_texture_free (t);
}
g_object_unref (subtex);
slices[slice_index].rect.x = x;
@@ -1336,8 +1547,10 @@ create_texture_from_texture_destroy (gpointer data)
}
GdkTexture *
gsk_gl_driver_create_gdk_texture (GskGLDriver *self,
guint texture_id)
gsk_gl_driver_create_gdk_texture (GskGLDriver *self,
guint texture_id,
GdkGLTextureFlags flags,
GdkColorSpace *color_space)
{
GskGLTextureState *state;
GskGLTexture *texture;
@@ -1365,10 +1578,12 @@ gsk_gl_driver_create_gdk_texture (GskGLDriver *self,
texture->texture_id = 0;
gsk_gl_texture_free (texture);
return gdk_gl_texture_new (self->command_queue->context,
texture_id,
width,
height,
create_texture_from_texture_destroy,
state);
return gdk_gl_texture_new_with_color_space (self->command_queue->context,
texture_id,
width,
height,
flags,
color_space,
create_texture_from_texture_destroy,
state);
}

View File

@@ -155,7 +155,9 @@ void gsk_gl_driver_begin_frame (GskGLDriver *s
void gsk_gl_driver_end_frame (GskGLDriver *self);
void gsk_gl_driver_after_frame (GskGLDriver *self);
GdkTexture * gsk_gl_driver_create_gdk_texture (GskGLDriver *self,
guint texture_id);
guint texture_id,
GdkGLTextureFlags flags,
GdkColorSpace *color_space);
void gsk_gl_driver_cache_texture (GskGLDriver *self,
const GskTextureKey *key,
guint texture_id);

View File

@@ -22,12 +22,21 @@
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdkmemoryformatprivate.h>
#include <gdk/gdkcolorspaceprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include "gskglcommandqueueprivate.h"
#include "gskgldriverprivate.h"
#include "gskglglyphlibraryprivate.h"
#ifdef HAVE_PANGOFT
#include <pango/pangofc-font.h>
#include <cairo-ft.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_PARAMETER_TAGS_H
#endif
#define MAX_GLYPH_SIZE 128
G_DEFINE_TYPE (GskGLGlyphLibrary, gsk_gl_glyph_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
@@ -209,6 +218,8 @@ gsk_gl_glyph_library_create_surface (GskGLGlyphLibrary *self,
return surface;
}
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
static void
render_glyph (cairo_surface_t *surface,
const GskGLGlyphKey *key,
@@ -217,6 +228,14 @@ render_glyph (cairo_surface_t *surface,
cairo_t *cr;
PangoGlyphString glyph_string;
PangoGlyphInfo glyph_info;
#ifdef HAVE_PANGOFT
FT_Face face;
FT_Bool darken = 1;
FT_Parameter property = { FT_PARAM_TAG_STEM_DARKENING, &darken };
face = pango_fc_font_lock_face (PANGO_FC_FONT (key->font));
FT_Face_Properties (face, 1, &property);
#endif
g_assert (surface != NULL);
@@ -235,8 +254,14 @@ render_glyph (cairo_surface_t *surface,
cairo_destroy (cr);
cairo_surface_flush (surface);
#ifdef HAVE_PANGOFT
pango_fc_font_unlock_face (PANGO_FC_FONT (key->font));
#endif
}
G_GNUC_END_IGNORE_DEPRECATIONS
static void
gsk_gl_glyph_library_upload_glyph (GskGLGlyphLibrary *self,
const GskGLGlyphKey *key,
@@ -280,9 +305,11 @@ gsk_gl_glyph_library_upload_glyph (GskGLGlyphLibrary *self,
pixel_data = free_data = g_malloc (width * height * 4);
gdk_memory_convert (pixel_data, width * 4,
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
gdk_color_space_get_srgb_linear (),
cairo_image_surface_get_data (surface),
stride,
GDK_MEMORY_DEFAULT,
gdk_color_space_get_srgb_linear (),
width, height);
stride = width * 4;
gl_format = GL_RGBA;

View File

@@ -23,7 +23,9 @@
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdkmemoryformatprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdkcolorspaceprivate.h>
#include <gdk/gdktextureprivate.h>
#include <gdk/gdkmemorytextureprivate.h>
#include "gskglcommandqueueprivate.h"
#include "gskgldriverprivate.h"
@@ -71,25 +73,107 @@ gsk_gl_icon_library_init (GskGLIconLibrary *self)
gsk_gl_icon_data_free);
}
static GdkMemoryTexture *
gsk_ngl_texture_prepare_upload (GdkGLContext *context,
GdkTexture *texture,
GLenum *gl_internalformat,
GLenum *gl_format,
GLenum *gl_type)
{
GdkMemoryTexture *memtex;
GdkColorSpace *color_space;
GdkMemoryFormat format;
format = gdk_texture_get_format (texture);
color_space = gdk_texture_get_color_space (texture);
memtex = gdk_memory_texture_from_texture (texture, format, color_space);
if (!gdk_memory_format_gl_format (format,
gdk_gl_context_get_use_es (context),
gl_internalformat,
gl_format,
gl_type))
{
g_object_unref (memtex);
format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
if (!gdk_memory_format_gl_format (format,
gdk_gl_context_get_use_es (context),
gl_internalformat,
gl_format,
gl_type))
{
g_assert_not_reached ();
}
}
return gdk_memory_texture_from_texture (texture, format, gdk_color_space_get_srgb_linear ());
}
static void
straightTexSubImage2D (int tex,
int level,
int x,
int y,
int width,
int height,
GLenum gl_format,
GLenum gl_type,
const guchar *data,
gsize stride)
{
glTexSubImage2D (tex, level,
x, y,
width, height,
gl_format, gl_type,
data);
}
static void
strideTexSubImage2D (int tex,
int level,
int x,
int y,
int width,
int height,
GLenum gl_format,
GLenum gl_type,
const guchar *data,
gsize stride)
{
for (int i = 0; i < height; i++)
{
glTexSubImage2D (tex, level,
x, y + i,
width, height,
gl_format, gl_type,
data + i * stride);
}
}
void
gsk_gl_icon_library_add (GskGLIconLibrary *self,
GdkTexture *key,
const GskGLIconData **out_value)
{
GskGLTextureLibrary *tl = (GskGLTextureLibrary *)self;
GdkGLContext *context = gdk_gl_context_get_current ();
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
cairo_surface_t *surface;
GdkMemoryTexture *memtex;
GskGLIconData *icon_data;
guint8 *pixel_data;
guint8 *surface_data;
guint8 *free_data = NULL;
guint gl_format;
guint gl_type;
const guchar *pixel_data;
gsize stride, bpp;
GdkMemoryFormat format;
GLenum gl_internalformat;
GLenum gl_format;
GLenum gl_type;
guint packed_x;
guint packed_y;
int width;
int height;
guint texture_id;
void (* upload_func) (int, int, int, int, int, int, GLenum, GLenum, const guchar *, gsize);
g_assert (GSK_IS_GL_ICON_LIBRARY (self));
g_assert (GDK_IS_TEXTURE (key));
@@ -106,104 +190,114 @@ gsk_gl_icon_library_add (GskGLIconLibrary *self,
icon_data->source_texture = g_object_ref (key);
/* actually upload the texture */
surface = gdk_texture_download_surface (key);
surface_data = cairo_image_surface_get_data (surface);
gdk_gl_context_push_debug_group_printf (gdk_gl_context_get_current (),
"Uploading texture");
gdk_gl_context_push_debug_group_printf (context, "Uploading texture");
memtex = gsk_ngl_texture_prepare_upload (context,
key,
&gl_internalformat,
&gl_format,
&gl_type);
if (gdk_gl_context_get_use_es (gdk_gl_context_get_current ()))
{
pixel_data = free_data = g_malloc (width * height * 4);
gdk_memory_convert (pixel_data, width * 4,
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
surface_data, cairo_image_surface_get_stride (surface),
GDK_MEMORY_DEFAULT, width, height);
gl_format = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;
}
else
{
pixel_data = surface_data;
gl_format = GL_BGRA;
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
}
pixel_data = gdk_memory_texture_get_data (memtex);
stride = gdk_memory_texture_get_stride (memtex);
format = gdk_texture_get_format (GDK_TEXTURE (memtex));
bpp = gdk_memory_format_bytes_per_pixel (format);
texture_id = GSK_GL_TEXTURE_ATLAS_ENTRY_TEXTURE (icon_data);
glBindTexture (GL_TEXTURE_2D, texture_id);
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + 1, packed_y + 1,
width, height,
gl_format, gl_type,
pixel_data);
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
/* GL_UNPACK_ROW_LENGTH is available on desktop GL, OpenGL ES >= 3.0, or if
* the GL_EXT_unpack_subimage extension for OpenGL ES 2.0 is available
*/
if ((stride % bpp == 0) &&
(gdk_gl_context_get_api (context) == GDK_GL_API_GL ||
gdk_gl_context_check_version (context, 3, 0, 3, 0) ||
gdk_gl_context_has_unpack_subimage (context)))
{
upload_func = straightTexSubImage2D;
glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp);
}
else
{
upload_func = strideTexSubImage2D;
}
upload_func (GL_TEXTURE_2D, 0,
packed_x + 1, packed_y + 1,
width, height,
gl_format, gl_type,
pixel_data,
stride);
/* Padding top */
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + 1, packed_y,
width, 1,
gl_format, gl_type,
pixel_data);
upload_func (GL_TEXTURE_2D, 0,
packed_x + 1, packed_y,
width, 1,
gl_format, gl_type,
pixel_data,
stride);
/* Padding left */
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x, packed_y + 1,
1, height,
gl_format, gl_type,
pixel_data);
upload_func (GL_TEXTURE_2D, 0,
packed_x, packed_y + 1,
1, height,
gl_format, gl_type,
pixel_data,
stride);
/* Padding top left */
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x, packed_y,
1, 1,
gl_format, gl_type,
pixel_data);
upload_func (GL_TEXTURE_2D, 0,
packed_x, packed_y,
1, 1,
gl_format, gl_type,
pixel_data,
stride);
/* Padding right */
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + width + 1, packed_y + 1,
1, height,
gl_format, gl_type,
pixel_data);
upload_func (GL_TEXTURE_2D, 0,
packed_x + width + 1, packed_y + 1,
1, height,
gl_format, gl_type,
pixel_data + (width - 1) * bpp,
stride);
/* Padding top right */
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + width + 1, packed_y,
1, 1,
gl_format, gl_type,
pixel_data);
upload_func (GL_TEXTURE_2D, 0,
packed_x + width + 1, packed_y,
1, 1,
gl_format, gl_type,
pixel_data + (width - 1) * bpp,
stride);
/* Padding bottom */
glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei (GL_UNPACK_SKIP_ROWS, height - 1);
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + 1, packed_y + 1 + height,
width, 1,
gl_format, gl_type,
pixel_data);
upload_func (GL_TEXTURE_2D, 0,
packed_x + 1, packed_y + 1 + height,
width, 1,
gl_format, gl_type,
pixel_data + (height - 1) * stride,
stride);
/* Padding bottom left */
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x, packed_y + 1 + height,
1, 1,
gl_format, gl_type,
pixel_data);
upload_func (GL_TEXTURE_2D, 0,
packed_x, packed_y + 1 + height,
1, 1,
gl_format, gl_type,
pixel_data + (height - 1) * stride,
stride);
/* Padding bottom right */
glPixelStorei (GL_UNPACK_ROW_LENGTH, width);
glPixelStorei (GL_UNPACK_SKIP_PIXELS, width - 1);
glTexSubImage2D (GL_TEXTURE_2D, 0,
packed_x + 1 + width, packed_y + 1 + height,
1, 1,
gl_format, gl_type,
pixel_data);
upload_func (GL_TEXTURE_2D, 0,
packed_x + 1 + width, packed_y + 1 + height,
1, 1,
gl_format, gl_type,
pixel_data + (width - 1) * bpp + (height - 1) * stride,
stride);
/* Reset this */
glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
if ((stride % bpp == 0) &&
(gdk_gl_context_get_api (context) == GDK_GL_API_GL ||
gdk_gl_context_check_version (context, 3, 0, 3, 0) ||
gdk_gl_context_has_unpack_subimage (context)))
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
gdk_gl_context_pop_debug_group (gdk_gl_context_get_current ());
gdk_gl_context_pop_debug_group (context);
*out_value = icon_data;
cairo_surface_destroy (surface);
g_free (free_data);
g_object_unref (memtex);
tl->driver->command_queue->n_uploads++;

View File

@@ -82,3 +82,19 @@ GSK_GL_DEFINE_PROGRAM (unblurred_outset_shadow,
GSK_GL_ADD_UNIFORM (1, UNBLURRED_OUTSET_SHADOW_SPREAD, u_spread)
GSK_GL_ADD_UNIFORM (2, UNBLURRED_OUTSET_SHADOW_OFFSET, u_offset)
GSK_GL_ADD_UNIFORM (3, UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect))
GSK_GL_DEFINE_PROGRAM (postprocessing,
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("postprocessing.glsl")),
GSK_GL_NO_UNIFORMS)
GSK_GL_DEFINE_PROGRAM (linearize,
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("linearize.glsl")),
GSK_GL_NO_UNIFORMS)
GSK_GL_DEFINE_PROGRAM (premultiply,
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("premultiply.glsl")),
GSK_GL_NO_UNIFORMS)
GSK_GL_DEFINE_PROGRAM (linearize_premultiply,
GSK_GL_SHADER_SINGLE (GSK_GL_SHADER_RESOURCE ("linearize_premultiply.glsl")),
GSK_GL_NO_UNIFORMS)

View File

@@ -25,6 +25,7 @@
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdksurfaceprivate.h>
#include <glib/gi18n-lib.h>
#include <gdk/gdkcolorspaceprivate.h>
#include <gsk/gskdebugprivate.h>
#include <gsk/gskrendererprivate.h>
#include <gsk/gskrendernodeprivate.h>
@@ -390,7 +391,10 @@ gsk_gl_renderer_render_texture (GskRenderer *renderer,
#endif
gsk_gl_render_job_render_flipped (job, root);
texture_id = gsk_gl_driver_release_render_target (self->driver, render_target, FALSE);
texture = gsk_gl_driver_create_gdk_texture (self->driver, texture_id);
texture = gsk_gl_driver_create_gdk_texture (self->driver,
texture_id,
GDK_GL_TEXTURE_PREMULTIPLIED,
gdk_color_space_get_srgb_linear ());
gsk_gl_driver_end_frame (self->driver);
gsk_gl_render_job_free (job);

View File

@@ -26,6 +26,8 @@
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdkrgbaprivate.h>
#include <gdk/gdkcolorspaceprivate.h>
#include <gdk/gdkmemoryformatprivate.h>
#include <gsk/gskrendernodeprivate.h>
#include <gsk/gskglshaderprivate.h>
#include <gdk/gdktextureprivate.h>
@@ -988,11 +990,23 @@ gsk_gl_render_job_update_clip (GskGLRenderJob *job,
return TRUE;
}
/* Convert from sRGB floats to linear sRGB half-floats */
static inline void
rgba_to_half (const GdkRGBA *rgba,
guint16 h[4])
{
float_to_half4 ((const float *)rgba, h);
GdkRGBA d;
gdk_memory_convert ((guchar *) &d,
sizeof (float) * 4,
GDK_MEMORY_R32G32B32A32_FLOAT,
gdk_color_space_get_srgb_linear (),
(guchar *) rgba,
sizeof (float) * 4,
GDK_MEMORY_R32G32B32A32_FLOAT,
gdk_color_space_get_srgb (),
1,
1);
float_to_half4 ((const float *)&d, h);
}
/* fill_vertex_data */
@@ -1226,6 +1240,8 @@ gsk_gl_render_job_visit_as_fallback (GskGLRenderJob *job,
surface_width,
surface_height);
gdk_cairo_surface_set_color_space (rendered_surface, gdk_color_space_get_srgb_linear ());
cairo_surface_set_device_scale (rendered_surface, scale_x, scale_y);
cr = cairo_create (rendered_surface);
@@ -4050,6 +4066,21 @@ gsk_gl_render_job_render_flipped (GskGLRenderJob *job,
glDeleteTextures (1, &texture_id);
}
static void
gsk_gl_render_job_postprocess (GskGLRenderJob *job,
guint texture_id)
{
gsk_gl_command_queue_bind_framebuffer (job->command_queue, job->framebuffer);
gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
gsk_gl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, postprocessing));
gsk_gl_program_set_uniform_texture (job->current_program,
UNIFORM_SHARED_SOURCE, 0,
GL_TEXTURE_2D, GL_TEXTURE0, texture_id);
gsk_gl_render_job_draw_offscreen_rect (job, &job->viewport);
gsk_gl_render_job_end_draw (job);
}
void
gsk_gl_render_job_render (GskGLRenderJob *job,
GskRenderNode *root)
@@ -4057,6 +4088,8 @@ gsk_gl_render_job_render (GskGLRenderJob *job,
G_GNUC_UNUSED gint64 start_time;
guint scale_factor;
guint surface_height;
GskGLRenderTarget *render_target;
guint texture_id;
g_return_if_fail (job != NULL);
g_return_if_fail (root != NULL);
@@ -4067,16 +4100,28 @@ gsk_gl_render_job_render (GskGLRenderJob *job,
gsk_gl_command_queue_make_current (job->command_queue);
if (!gsk_gl_driver_create_render_target (job->driver,
job->viewport.size.width,
job->viewport.size.height,
job->target_format,
GL_NEAREST, GL_NEAREST,
&render_target))
g_assert_not_reached ();
/* Build the command queue using the shared GL context for all renderers
* on the same display.
*/
start_time = GDK_PROFILER_CURRENT_TIME;
gdk_gl_context_push_debug_group (job->command_queue->context, "Building command queue");
gsk_gl_command_queue_bind_framebuffer (job->command_queue, job->framebuffer);
gsk_gl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
if (job->clear_framebuffer)
gsk_gl_command_queue_clear (job->command_queue, 0, &job->viewport);
gsk_gl_render_job_visit_node (job, root);
gdk_gl_context_pop_debug_group (job->command_queue->context);
texture_id = gsk_gl_driver_release_render_target (job->driver, render_target, FALSE);
gsk_gl_render_job_postprocess (job, texture_id);
gdk_profiler_add_mark (start_time, GDK_PROFILER_CURRENT_TIME-start_time, "Build GL command queue", "");
#if 0

View File

@@ -0,0 +1,22 @@
// VERTEX_SHADER:
// linearize.glsl
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
}
// FRAGMENT_SHADER:
// linearize.glsl
void main() {
vec4 diffuse = GskTexture(u_source, vUv);
diffuse = gsk_unpremultiply(diffuse);
diffuse = gsk_srgb_to_linear(diffuse);
diffuse = gsk_premultiply(diffuse);
gskSetOutputColor(diffuse);
}

View File

@@ -0,0 +1,21 @@
// VERTEX_SHADER:
// linearize_premultiply.glsl
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
}
// FRAGMENT_SHADER:
// linearize_premultiply.glsl
void main() {
vec4 diffuse = GskTexture(u_source, vUv);
diffuse = gsk_srgb_to_linear(diffuse);
diffuse = gsk_premultiply(diffuse);
gskSetOutputColor(diffuse);
}

View File

@@ -0,0 +1,22 @@
// VERTEX_SHADER:
// postprocessing.glsl
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
}
// FRAGMENT_SHADER:
// postprocessing.glsl
void main() {
vec4 diffuse = GskTexture(u_source, vUv);
diffuse = gsk_unpremultiply(diffuse);
diffuse = gsk_linear_to_srgb(diffuse);
diffuse = gsk_premultiply(diffuse);
gskSetOutputColor(diffuse);
}

View File

@@ -42,10 +42,31 @@ gsk_get_bounds(vec4[3] data)
return vec4(data[0].xy, data[0].xy + data[0].zw);
}
vec4 gsk_premultiply(vec4 c) {
vec4 gsk_premultiply(vec4 c)
{
return vec4(c.rgb * c.a, c.a);
}
vec4 gsk_unpremultiply(vec4 c)
{
if (c.a != 0)
return vec4(c.rgb / c.a, c.a);
else
return c;
}
vec4 gsk_srgb_to_linear(vec4 srgb)
{
vec3 linear_rgb = pow(srgb.rgb, vec3(2.2));
return vec4(linear_rgb, srgb.a);
}
vec4 gsk_linear_to_srgb(vec4 linear_rgba)
{
vec3 srgb = pow(linear_rgba.rgb, vec3(1/2.2));
return vec4(srgb, linear_rgba.a);
}
vec4 gsk_scaled_premultiply(vec4 c, float s) {
// Fast version of gsk_premultiply(c) * s
// 4 muls instead of 7

View File

@@ -0,0 +1,20 @@
// VERTEX_SHADER:
// premultiply.glsl
void main() {
gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
vUv = vec2(aUv.x, aUv.y);
}
// FRAGMENT_SHADER:
// premultiply.glsl
void main() {
vec4 diffuse = GskTexture(u_source, vUv);
diffuse = gsk_premultiply(diffuse);
gskSetOutputColor(diffuse);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright © 2016 Endless
* Copyright © 2016 Endless
* 2018 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
@@ -25,6 +25,9 @@
#include "gskdebugprivate.h"
#include "gskrendererprivate.h"
#include "gskrendernodeprivate.h"
#include "gdk/gdkcolorspaceprivate.h"
#include "gdk/gdkmemorytextureprivate.h"
#include "gdk/gdktextureprivate.h"
#ifdef G_ENABLE_DEBUG
@@ -40,6 +43,7 @@ struct _GskCairoRenderer
GdkCairoContext *cairo_context;
gboolean color_managed;
#ifdef G_ENABLE_DEBUG
ProfileTimers profile_timers;
#endif
@@ -104,6 +108,7 @@ gsk_cairo_renderer_render_texture (GskRenderer *renderer,
GskRenderNode *root,
const graphene_rect_t *viewport)
{
GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
GdkTexture *texture;
cairo_surface_t *surface;
cairo_t *cr;
@@ -127,7 +132,7 @@ gsk_cairo_renderer_render_texture (GskRenderer *renderer,
{
for (x = 0; x < width; x += MAX_IMAGE_SIZE)
{
texture = gsk_cairo_renderer_render_texture (renderer, root,
texture = gsk_cairo_renderer_render_texture (renderer, root,
&GRAPHENE_RECT_INIT (x, y,
MIN (MAX_IMAGE_SIZE, viewport->size.width - x),
MIN (MAX_IMAGE_SIZE, viewport->size.height - y)));
@@ -145,6 +150,9 @@ gsk_cairo_renderer_render_texture (GskRenderer *renderer,
}
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
if (self->color_managed)
gdk_cairo_surface_set_color_space (surface, gdk_color_space_get_srgb_linear ());
cr = cairo_create (surface);
cairo_translate (cr, - viewport->origin.x, - viewport->origin.y);
@@ -189,7 +197,64 @@ gsk_cairo_renderer_render (GskRenderer *renderer,
}
#endif
gsk_cairo_renderer_do_render (renderer, cr, root);
if (!self->color_managed ||
gdk_color_space_is_linear (gdk_cairo_get_color_space (cr)))
{
gsk_cairo_renderer_do_render (renderer, cr, root);
}
else
{
GdkSurface *surface = gsk_renderer_get_surface (renderer);
GdkColorSpace *target_space = gdk_cairo_get_color_space (cr);
cairo_surface_t *cairo_surface;
cairo_t *cr2;
GdkTexture *color_correct;
const cairo_region_t *frame_region;
cairo_rectangle_int_t extents;
guint i, n;
frame_region = gdk_draw_context_get_frame_region (GDK_DRAW_CONTEXT (self->cairo_context));
cairo_region_get_extents (frame_region, &extents);
/* We can't use cairo_push_group() here, because we'd lose the
* color profile information.
*/
cairo_surface = gdk_surface_create_similar_surface (surface,
CAIRO_CONTENT_COLOR_ALPHA,
extents.width,
extents.height);
gdk_cairo_surface_set_color_space (cairo_surface,
gdk_color_space_get_srgb_linear ());
cr2 = cairo_create (cairo_surface);
cairo_translate (cr2, -extents.x, -extents.y);
gdk_cairo_region (cr2, frame_region);
cairo_clip (cr2);
gsk_cairo_renderer_do_render (renderer, cr2, root);
cairo_destroy (cr2);
color_correct = gdk_texture_new_for_surface (cairo_surface);
cairo_surface_destroy (cairo_surface);
n = cairo_region_num_rectangles (frame_region);
for (i = 0; i < n; i++)
{
cairo_rectangle_int_t rect;
GdkTexture *sub;
cairo_region_get_rectangle (frame_region, i, &rect);
rect.x -= extents.x;
rect.y -= extents.y;
sub = gdk_memory_texture_new_subtexture (GDK_MEMORY_TEXTURE (color_correct),
rect.x, rect.y, rect.width, rect.height);
cairo_surface = gdk_texture_download_surface (sub, target_space);
cairo_set_source_surface (cr, cairo_surface, rect.x + extents.x, rect.y + extents.y);
cairo_paint (cr);
cairo_surface_destroy (cairo_surface);
g_object_unref (sub);
}
g_object_unref (color_correct);
}
cairo_destroy (cr);
@@ -210,6 +275,8 @@ gsk_cairo_renderer_class_init (GskCairoRendererClass *klass)
static void
gsk_cairo_renderer_init (GskCairoRenderer *self)
{
self->color_managed = TRUE;
#ifdef G_ENABLE_DEBUG
GskProfiler *profiler = gsk_renderer_get_profiler (GSK_RENDERER (self));

View File

@@ -27,12 +27,20 @@
#include "gskroundedrectprivate.h"
#include "gsktransformprivate.h"
#include "gdk/gdkcolorprivate.h"
#include "gdk/gdktextureprivate.h"
#include "gdk/gdkmemoryformatprivate.h"
#include "gdk/gdkprivate.h"
#include <hb-ot.h>
#ifdef HAVE_PANGOFT
#include <pango/pangofc-font.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_PARAMETER_TAGS_H
#endif
/* maximal number of rectangles we keep in a diff region before we throw
* the towel and just use the bounding box of the parent node.
* Meant to avoid performance corner cases.
@@ -187,6 +195,7 @@ gsk_linear_gradient_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskLinearGradientNode *self = (GskLinearGradientNode *) node;
GdkColorSpace *color_space;
cairo_pattern_t *pattern;
gsize i;
@@ -196,14 +205,20 @@ gsk_linear_gradient_node_draw (GskRenderNode *node,
if (gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE)
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
color_space = gdk_cairo_get_color_space (cr);
for (i = 0; i < self->n_stops; i++)
{
GdkColor color;
const float *components;
gdk_color_convert_rgba (&color, color_space, &self->stops[i].color);
components = gdk_color_get_components (&color);
cairo_pattern_add_color_stop_rgba (pattern,
self->stops[i].offset,
self->stops[i].color.red,
self->stops[i].color.green,
self->stops[i].color.blue,
self->stops[i].color.alpha);
components[0],
components[1],
components[2],
gdk_color_get_alpha (&color));
gdk_color_finish (&color);
}
cairo_set_source (cr, pattern);
@@ -464,6 +479,7 @@ gsk_radial_gradient_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskRadialGradientNode *self = (GskRadialGradientNode *) node;
GdkColorSpace *color_space;
cairo_pattern_t *pattern;
gsize i;
@@ -483,13 +499,21 @@ gsk_radial_gradient_node_draw (GskRenderNode *node,
else
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
color_space = gdk_cairo_get_color_space (cr);
for (i = 0; i < self->n_stops; i++)
cairo_pattern_add_color_stop_rgba (pattern,
self->stops[i].offset,
self->stops[i].color.red,
self->stops[i].color.green,
self->stops[i].color.blue,
self->stops[i].color.alpha);
{
GdkColor color;
const float *components;
gdk_color_convert_rgba (&color, color_space, &self->stops[i].color);
components = gdk_color_get_components (&color);
cairo_pattern_add_color_stop_rgba (pattern,
self->stops[i].offset,
components[0],
components[1],
components[2],
gdk_color_get_alpha (&color));
gdk_color_finish (&color);
}
gsk_cairo_rectangle (cr, &node->bounds);
cairo_translate (cr, self->center.x, self->center.y);
@@ -824,11 +848,17 @@ gsk_conic_gradient_node_finalize (GskRenderNode *node)
#define DEG_TO_RAD(x) ((x) * (G_PI / 180.f))
static void
_cairo_mesh_pattern_set_corner_rgba (cairo_pattern_t *pattern,
guint corner_num,
const GdkRGBA *rgba)
_cairo_mesh_pattern_set_corner_color (cairo_pattern_t *pattern,
guint corner_num,
const GdkColor *color)
{
cairo_mesh_pattern_set_corner_color_rgba (pattern, corner_num, rgba->red, rgba->green, rgba->blue, rgba->alpha);
const float *components = gdk_color_get_components (color);
cairo_mesh_pattern_set_corner_color_rgba (pattern,
corner_num,
components[0],
components[1],
components[2],
gdk_color_get_alpha (color));
}
static void
@@ -853,9 +883,9 @@ static void
gsk_conic_gradient_node_add_patch (cairo_pattern_t *pattern,
float radius,
float start_angle,
const GdkRGBA *start_color,
const GdkColor *start_color,
float end_angle,
const GdkRGBA *end_color)
const GdkColor *end_color)
{
double x, y;
@@ -868,47 +898,27 @@ gsk_conic_gradient_node_add_patch (cairo_pattern_t *pattern,
cairo_mesh_pattern_line_to (pattern, x, y);
cairo_mesh_pattern_line_to (pattern, 0, 0);
_cairo_mesh_pattern_set_corner_rgba (pattern, 0, start_color);
_cairo_mesh_pattern_set_corner_rgba (pattern, 1, start_color);
_cairo_mesh_pattern_set_corner_rgba (pattern, 2, end_color);
_cairo_mesh_pattern_set_corner_rgba (pattern, 3, end_color);
_cairo_mesh_pattern_set_corner_color (pattern, 0, start_color);
_cairo_mesh_pattern_set_corner_color (pattern, 1, start_color);
_cairo_mesh_pattern_set_corner_color (pattern, 2, end_color);
_cairo_mesh_pattern_set_corner_color (pattern, 3, end_color);
cairo_mesh_pattern_end_patch (pattern);
}
static void
gdk_rgba_color_interpolate (GdkRGBA *dest,
const GdkRGBA *src1,
const GdkRGBA *src2,
double progress)
{
double alpha = src1->alpha * (1.0 - progress) + src2->alpha * progress;
dest->alpha = alpha;
if (alpha == 0)
{
dest->red = src1->red * (1.0 - progress) + src2->red * progress;
dest->green = src1->green * (1.0 - progress) + src2->green * progress;
dest->blue = src1->blue * (1.0 - progress) + src2->blue * progress;
}
else
{
dest->red = (src1->red * src1->alpha * (1.0 - progress) + src2->red * src2->alpha * progress) / alpha;
dest->green = (src1->green * src1->alpha * (1.0 - progress) + src2->green * src2->alpha * progress) / alpha;
dest->blue = (src1->blue * src1->alpha * (1.0 - progress) + src2->blue * src2->alpha * progress) / alpha;
}
}
static void
gsk_conic_gradient_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskConicGradientNode *self = (GskConicGradientNode *) node;
GdkColorSpace *color_space;
cairo_pattern_t *pattern;
graphene_point_t corner;
float radius;
gsize i;
color_space = gdk_cairo_get_color_space (cr);
pattern = cairo_pattern_create_mesh ();
graphene_rect_get_top_right (&node->bounds, &corner);
radius = graphene_point_distance (&self->center, &corner, NULL, NULL);
@@ -923,26 +933,31 @@ gsk_conic_gradient_node_draw (GskRenderNode *node,
{
GskColorStop *stop1 = &self->stops[MAX (i, 1) - 1];
GskColorStop *stop2 = &self->stops[MIN (i, self->n_stops - 1)];
GdkColor stop1_color, stop2_color;
double offset1 = i > 0 ? stop1->offset : 0;
double offset2 = i < self->n_stops ? stop2->offset : 1;
double start_angle, end_angle;
offset1 = offset1 * 360 + self->rotation - 90;
offset2 = offset2 * 360 + self->rotation - 90;
gdk_color_convert_rgba (&stop1_color, color_space, &stop1->color);
gdk_color_convert_rgba (&stop2_color, color_space, &stop1->color);
for (start_angle = offset1; start_angle < offset2; start_angle = end_angle)
{
GdkRGBA start_color, end_color;
GdkColor start_color, end_color;
end_angle = (floor (start_angle / 45) + 1) * 45;
end_angle = MIN (end_angle, offset2);
gdk_rgba_color_interpolate (&start_color,
&stop1->color,
&stop2->color,
(start_angle - offset1) / (offset2 - offset1));
gdk_rgba_color_interpolate (&end_color,
&stop1->color,
&stop2->color,
(end_angle - offset1) / (offset2 - offset1));
gdk_color_mix (&start_color,
color_space,
&stop1_color,
&stop2_color,
(start_angle - offset1) / (offset2 - offset1));
gdk_color_mix (&end_color,
color_space,
&stop1_color,
&stop2_color,
(end_angle - offset1) / (offset2 - offset1));
gsk_conic_gradient_node_add_patch (pattern,
radius,
@@ -951,6 +966,9 @@ gsk_conic_gradient_node_draw (GskRenderNode *node,
DEG_TO_RAD (end_angle),
&end_color);
}
gdk_color_finish (&stop2_color);
gdk_color_finish (&stop1_color);
}
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
@@ -1167,7 +1185,8 @@ struct _GskBorderNode
static void
gsk_border_node_mesh_add_patch (cairo_pattern_t *pattern,
const GdkRGBA *color,
GdkColorSpace *color_space,
const GdkRGBA *rgba,
double x0,
double y0,
double x1,
@@ -1177,15 +1196,23 @@ gsk_border_node_mesh_add_patch (cairo_pattern_t *pattern,
double x3,
double y3)
{
GdkColor color;
const float *components;
float alpha;
gdk_color_convert_rgba (&color, color_space, rgba);
components = gdk_color_get_components (&color);
alpha = gdk_color_get_alpha (&color);
cairo_mesh_pattern_begin_patch (pattern);
cairo_mesh_pattern_move_to (pattern, x0, y0);
cairo_mesh_pattern_line_to (pattern, x1, y1);
cairo_mesh_pattern_line_to (pattern, x2, y2);
cairo_mesh_pattern_line_to (pattern, x3, y3);
cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, color->red, color->green, color->blue, color->alpha);
cairo_mesh_pattern_set_corner_color_rgba (pattern, 1, color->red, color->green, color->blue, color->alpha);
cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, color->red, color->green, color->blue, color->alpha);
cairo_mesh_pattern_set_corner_color_rgba (pattern, 3, color->red, color->green, color->blue, color->alpha);
cairo_mesh_pattern_set_corner_color_rgba (pattern, 0, components[0], components[1], components[2], alpha);
cairo_mesh_pattern_set_corner_color_rgba (pattern, 1, components[0], components[1], components[2], alpha);
cairo_mesh_pattern_set_corner_color_rgba (pattern, 2, components[0], components[1], components[2], alpha);
cairo_mesh_pattern_set_corner_color_rgba (pattern, 3, components[0], components[1], components[2], alpha);
cairo_mesh_pattern_end_patch (pattern);
}
@@ -1231,11 +1258,13 @@ gsk_border_node_draw (GskRenderNode *node,
* Note that the call to cairo_fill() will add the potential final
* segment by closing the path, so we don't have to care.
*/
GdkColorSpace *color_space;
cairo_pattern_t *mesh;
cairo_matrix_t mat;
graphene_point_t tl, br;
float scale;
color_space = gdk_cairo_get_color_space (cr);
mesh = cairo_pattern_create_mesh ();
cairo_matrix_init_translate (&mat, -bounds->origin.x, -bounds->origin.y);
cairo_pattern_set_matrix (mesh, &mat);
@@ -1253,6 +1282,7 @@ gsk_border_node_draw (GskRenderNode *node,
if (self->border_width[0] > 0)
{
gsk_border_node_mesh_add_patch (mesh,
color_space,
&self->border_color[0],
0, 0,
tl.x, tl.y,
@@ -1264,6 +1294,7 @@ gsk_border_node_draw (GskRenderNode *node,
if (self->border_width[1] > 0)
{
gsk_border_node_mesh_add_patch (mesh,
color_space,
&self->border_color[1],
bounds->size.width, 0,
br.x, tl.y,
@@ -1275,6 +1306,7 @@ gsk_border_node_draw (GskRenderNode *node,
if (self->border_width[2] > 0)
{
gsk_border_node_mesh_add_patch (mesh,
color_space,
&self->border_color[2],
0, bounds->size.height,
tl.x, br.y,
@@ -1286,6 +1318,7 @@ gsk_border_node_draw (GskRenderNode *node,
if (self->border_width[3] > 0)
{
gsk_border_node_mesh_add_patch (mesh,
color_space,
&self->border_color[3],
0, 0,
tl.x, tl.y,
@@ -1493,7 +1526,7 @@ gsk_texture_node_draw (GskRenderNode *node,
cairo_pattern_t *pattern;
cairo_matrix_t matrix;
surface = gdk_texture_download_surface (self->texture);
surface = gdk_texture_download_surface (self->texture, gdk_cairo_get_color_space (cr));
pattern = cairo_pattern_create_for_surface (surface);
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
@@ -4428,12 +4461,22 @@ gsk_text_node_finalize (GskRenderNode *node)
parent_class->finalize (node);
}
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
static void
gsk_text_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskTextNode *self = (GskTextNode *) node;
PangoGlyphString glyphs;
#ifdef HAVE_PANGOFT
FT_Face face;
FT_Bool darken = 1;
FT_Parameter property = { FT_PARAM_TAG_STEM_DARKENING, &darken };
face = pango_fc_font_lock_face (PANGO_FC_FONT (self->font));
FT_Face_Properties (face, 1, &property);
#endif
glyphs.num_glyphs = self->num_glyphs;
glyphs.glyphs = self->glyphs;
@@ -4446,8 +4489,14 @@ gsk_text_node_draw (GskRenderNode *node,
pango_cairo_show_glyph_string (cr, self->font, &glyphs);
cairo_restore (cr);
#ifdef HAVE_PANGOFT
pango_fc_font_unlock_face (PANGO_FC_FONT (self->font));
#endif
}
G_GNUC_END_IGNORE_DEPRECATIONS
static void
gsk_text_node_diff (GskRenderNode *node1,
GskRenderNode *node2,

View File

@@ -1416,7 +1416,7 @@ parse_cairo_node (GtkCssParser *parser)
else if (pixels != NULL)
{
cairo_t *cr = gsk_cairo_node_get_draw_context (node);
surface = gdk_texture_download_surface (pixels);
surface = gdk_texture_download_surface (pixels, gdk_texture_get_color_space (pixels));
cairo_set_source_surface (cr, surface, 0, 0);
cairo_paint (cr);
cairo_destroy (cr);

View File

@@ -19,6 +19,10 @@ gsk_private_gl_shaders = [
'gl/resources/repeat.glsl',
'gl/resources/custom.glsl',
'gl/resources/filled_border.glsl',
'gl/resources/postprocessing.glsl',
'gl/resources/linearize.glsl',
'gl/resources/premultiply.glsl',
'gl/resources/linearize_premultiply.glsl',
]
gsk_public_sources = files([

View File

@@ -342,7 +342,7 @@ gsk_vulkan_renderer_ref_texture_image (GskVulkanRenderer *self,
if (data)
return g_object_ref (data->image);
surface = gdk_texture_download_surface (texture);
surface = gdk_texture_download_surface (texture,NULL);
image = gsk_vulkan_image_new_from_data (uploader,
cairo_image_surface_get_data (surface),
cairo_image_surface_get_width (surface),

View File

@@ -735,24 +735,19 @@ gtk_gl_area_snapshot (GtkWidget *widget,
priv->texture = NULL;
priv->textures = g_list_prepend (priv->textures, texture);
texture->holder = gdk_gl_texture_new (priv->context,
texture->id,
texture->width,
texture->height,
release_texture, texture);
texture->holder = gdk_gl_texture_new_with_color_space (priv->context,
texture->id,
texture->width,
texture->height,
GDK_GL_TEXTURE_PREMULTIPLIED|GDK_GL_TEXTURE_FLIPPED,
gdk_color_space_get_srgb (),
release_texture, texture);
/* Our texture is rendered by OpenGL, so it is upside down,
* compared to what GSK expects, so flip it back.
*/
gtk_snapshot_save (snapshot);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (0, gtk_widget_get_height (widget)));
gtk_snapshot_scale (snapshot, 1, -1);
gtk_snapshot_append_texture (snapshot,
texture->holder,
&GRAPHENE_RECT_INIT (0, 0,
gtk_widget_get_width (widget),
gtk_widget_get_height (widget)));
gtk_snapshot_restore (snapshot);
g_object_unref (texture->holder);
}

View File

@@ -17,6 +17,7 @@ fribidi_req = '>= 0.19.7'
cairo_req = '>= 1.14.0'
gdk_pixbuf_req = '>= 2.30.0'
introspection_req = '>= 1.39.0'
lcms2_req = '>= 2.8'
wayland_proto_req = '>= 1.25'
wayland_req = '>= 1.21.0'
graphene_req = '>= 1.9.1'
@@ -398,6 +399,8 @@ fribidi_dep = dependency('fribidi', version: fribidi_req,
harfbuzz_dep = dependency('harfbuzz', version: harfbuzz_req,
fallback: ['harfbuzz', 'libharfbuzz_dep'],
default_options: ['coretext=enabled'])
lcms2_dep = dependency('lcms2', version: lcms2_req,
fallback : ['lcms2', 'liblcms2_dep'])
# Require PangoFT2 if on X11 or wayland
require_pangoft2 = wayland_enabled or x11_enabled
@@ -430,6 +433,7 @@ tiff_dep = dependency(is_msvc_like ? 'tiff' : 'libtiff-4',
jpeg_dep = dependency(is_msvc_like ? 'jpeg' : 'libjpeg',
fallback: ['libjpeg-turbo', 'jpeg_dep'],
required: true)
libheif_dep = dependency('libheif', required: false)
epoxy_dep = dependency('epoxy', version: epoxy_req,
fallback: ['libepoxy', 'libepoxy_dep'])

View File

@@ -290,17 +290,20 @@ gtk_gst_sink_texture_from_buffer (GtkGstSink *self,
GstGLSyncMeta *sync_meta;
sync_meta = gst_buffer_get_gl_sync_meta (buffer);
if (sync_meta) {
gst_gl_sync_meta_set_sync_point (sync_meta, self->gst_context);
gst_gl_sync_meta_wait (sync_meta, self->gst_context);
}
if (sync_meta)
{
gst_gl_sync_meta_set_sync_point (sync_meta, self->gst_context);
gst_gl_sync_meta_wait (sync_meta, self->gst_context);
}
texture = gdk_gl_texture_new (self->gdk_context,
*(guint *) frame->data[0],
frame->info.width,
frame->info.height,
(GDestroyNotify) video_frame_free,
frame);
texture = gdk_gl_texture_new_with_color_space (self->gdk_context,
*(guint *) frame->data[0],
frame->info.width,
frame->info.height,
0, /* Neither premultiplied nor flipped */
gdk_color_space_get_srgb (),
(GDestroyNotify) video_frame_free,
frame);
*pixel_aspect_ratio = ((double) frame->info.par_n) / ((double) frame->info.par_d);
}

6
subprojects/lcms2.wrap Normal file
View File

@@ -0,0 +1,6 @@
[wrap-git]
directory=lcms2
url=https://github.com/mm2/Little-CMS.git
revision=master
patch_directory=lcms2
depth=1

View File

@@ -0,0 +1,14 @@
project('lcms2', 'c',
version : '2.12',
meson_version : '>=0.56.0',
)
mod = import('unstable_external_project')
p = mod.add_project('configure',
configure_options : ['--prefix=@PREFIX@',
'--libdir=@PREFIX@/@LIBDIR@',
],
)
liblcms2_dep = p.dependency('lcms2')

View File

@@ -149,3 +149,10 @@ if libsysprof_dep.found()
dependencies: [libsysprof_dep, platform_gio_dep, libm],
)
endif
if libheif_dep.found()
executable('testheif',
sources: 'testheif.c',
dependencies: [libgtk_dep, libheif_dep],
)
endif

289
tests/testheif.c Normal file
View File

@@ -0,0 +1,289 @@
#include <gtk/gtk.h>
#include <libheif/heif.h>
#include <lcms2.h>
static void
describe_nclx_color_profile (const struct heif_color_profile_nclx *nclx,
GString *s)
{
if (nclx->color_primaries == heif_color_primaries_unspecified)
return;
if (nclx->color_primaries == heif_color_primaries_ITU_R_BT_709_5)
{
if (nclx->transfer_characteristics == heif_transfer_characteristic_IEC_61966_2_1 &&
(nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G ||
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_601_6))
{
g_string_append (s, "sRGB");
return;
}
if (nclx->transfer_characteristics == heif_transfer_characteristic_linear &&
(nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_470_6_System_B_G ||
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_601_6))
{
g_string_append (s, "sRGB linear");
return;
}
}
if (nclx->color_primaries == heif_color_primaries_ITU_R_BT_2020_2_and_2100_0)
{
if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_PQ &&
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance)
{
g_string_append (s, "BT.2020 PQ");
return;
}
if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_HLG &&
nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance)
{
g_string_append (s, "BT.2020 HLG");
return;
}
}
if (nclx->color_primaries == heif_color_primaries_SMPTE_EG_432_1)
{
if (nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_PQ)
{
g_string_append (s, "P3 PQ");
return;
}
}
g_string_append_printf (s, "%d/%d/%d",
nclx->color_primaries,
nclx->matrix_coefficients,
nclx->transfer_characteristics);
}
static GdkTexture *
load_heif_image (const char *filename,
GString *details,
GError **error)
{
struct heif_context *ctx;
struct heif_error err;
struct heif_image_handle *handle = NULL;
struct heif_image *image = NULL;
enum heif_chroma chroma;
GdkMemoryFormat format;
const char *format_name;
uint8_t *data = NULL;
int width, height, bpp, stride, bits;
GBytes *bytes;
GdkTexture *texture = NULL;
GdkColorSpace *color_space = NULL;
char *profile_type = NULL;
ctx = heif_context_alloc ();
err = heif_context_read_from_file (ctx, filename, NULL);
if (err.code != heif_error_Ok)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
goto out;
}
err = heif_context_get_primary_image_handle (ctx, &handle);
if (err.code != heif_error_Ok)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
goto out;
}
switch (heif_image_handle_get_color_profile_type (handle))
{
case heif_color_profile_type_not_present:
break;
case heif_color_profile_type_rICC:
case heif_color_profile_type_prof:
{
guchar *icc_data;
gsize icc_size;
profile_type = g_strdup ("icc");
icc_size = heif_image_handle_get_raw_color_profile_size (handle);
icc_data = g_new0 (guchar, icc_size);
err = heif_image_handle_get_raw_color_profile (handle, icc_data);
if (err.code == heif_error_Ok)
{
bytes = g_bytes_new (icc_data, icc_size);
color_space = gdk_color_space_new_from_icc_profile (bytes, NULL);
g_bytes_unref (bytes);
}
g_free (icc_data);
}
break;
case heif_color_profile_type_nclx:
{
struct heif_color_profile_nclx *nclx = NULL;
err = heif_image_handle_get_nclx_color_profile (handle, &nclx);
if (err.code == heif_error_Ok)
{
GString *s;
s = g_string_new ("");
describe_nclx_color_profile (nclx, s);
profile_type = g_string_free (s, FALSE);
color_space = gdk_color_space_new_from_cicp (nclx->color_primaries,
nclx->transfer_characteristics,
0,
TRUE);
heif_nclx_color_profile_free (nclx);
}
}
break;
default:
g_assert_not_reached ();
}
g_string_append_printf (details, "%d×%d pixels\n%d bits of luma, %d bits of chroma%s\n",
heif_image_handle_get_width (handle),
heif_image_handle_get_height (handle),
heif_image_handle_get_luma_bits_per_pixel (handle),
heif_image_handle_get_chroma_bits_per_pixel (handle),
heif_image_handle_has_alpha_channel (handle) ? ", with alpha" : "");
if (profile_type)
{
g_string_append_printf (details, "color profile: %s\n", profile_type);
g_free (profile_type);
}
if (color_space == NULL)
color_space = g_object_ref (gdk_color_space_get_srgb ());
if (heif_image_handle_has_alpha_channel (handle))
{
if (heif_image_handle_get_luma_bits_per_pixel (handle) > 8 ||
heif_image_handle_get_chroma_bits_per_pixel (handle) > 8)
{
chroma = heif_chroma_interleaved_RRGGBBAA_BE;
format = GDK_MEMORY_R16G16B16A16;
format_name = "R<sub>16</sub>G<sub>16</sub>B<sub>16</sub>A<sub>16</sub>";
}
else
{
chroma = heif_chroma_interleaved_RGBA;
format = GDK_MEMORY_R8G8B8A8;
format_name = "R<sub>8</sub>G<sub>8</sub>B<sub>8</sub>A<sub>8</sub>";
}
}
else
{
if (heif_image_handle_get_luma_bits_per_pixel (handle) > 8 ||
heif_image_handle_get_chroma_bits_per_pixel (handle) > 8)
{
chroma = heif_chroma_interleaved_RRGGBB_BE;
format = GDK_MEMORY_R16G16B16;
format_name = "R<sub>16</sub>G<sub>16</sub>B<sub>16</sub>";
}
else
{
chroma = heif_chroma_interleaved_RGB;
format = GDK_MEMORY_R8G8B8;
format_name = "R<sub>8</sub>G<sub>8</sub>B<sub>8</sub>";
}
}
err = heif_decode_image (handle, &image, heif_colorspace_RGB, chroma, NULL);
if (err.code != heif_error_Ok)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, err.message);
goto out;
}
width = heif_image_get_width (image, heif_channel_interleaved);
height = heif_image_get_height (image, heif_channel_interleaved);
bpp = heif_image_get_bits_per_pixel (image, heif_channel_interleaved);
data = (uint8_t*)heif_image_get_plane_readonly (image, heif_channel_interleaved, &stride);
bits = heif_image_get_bits_per_pixel_range (image, heif_channel_interleaved);
g_string_append_printf (details, "texture format %s", format_name);
g_print ("%s\n", details->str);
if (format == GDK_MEMORY_R16G16B16A16 || format == GDK_MEMORY_R16G16B16)
{
/* Shift image data to full 16bit range */
int shift = 16 - bits;
if (shift > 0)
{
for (int y = 0; y < height; ++y)
{
uint8_t *row = data + y * stride;
for (int x = 0; x < stride; x += 2)
{
uint8_t* p = &row[x];
int v = (p[0] << 8) | p[1];
v = (v << shift) | (v >> (16 - shift));
p[1] = (uint8_t) (v >> 8);
p[0] = (uint8_t) (v & 0xFF);
}
}
}
}
bytes = g_bytes_new_with_free_func (data, height * stride * bpp, (GDestroyNotify)heif_image_release, image);
image = NULL;
texture = gdk_memory_texture_new_with_color_space (width, height,
format, color_space,
bytes, stride);
g_bytes_unref (bytes);
g_object_unref (color_space);
out:
heif_context_free (ctx);
if (handle)
heif_image_handle_release (handle);
if (image)
heif_image_release (image);
return texture;
}
int main (int argc, char *argv[])
{
GdkTexture *texture;
GError *error = NULL;
GtkWidget *window, *picture, *box, *label;
GString *details;
gtk_init ();
details = g_string_new ("");
texture = load_heif_image (argv[1], details, &error);
if (!texture)
g_error ("%s", error->message);
window = gtk_window_new ();
gtk_window_set_title (GTK_WINDOW (window), argv[1]);
gtk_window_set_default_size (GTK_WINDOW (window), 1024, 768);
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
picture = gtk_picture_new_for_paintable (GDK_PAINTABLE (texture));
gtk_box_append (GTK_BOX (box), picture);
label = gtk_label_new (details->str);
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
gtk_widget_set_margin_bottom (label, 10);
gtk_box_append (GTK_BOX (box), label);
gtk_window_set_child (GTK_WINDOW (window), box);
g_string_free (details, TRUE);
gtk_window_present (GTK_WINDOW (window));
while (1)
g_main_context_iteration (NULL, TRUE);
}

BIN
testsuite/gdk/Rec709.icc Normal file

Binary file not shown.

43
testsuite/gdk/color.c Normal file
View File

@@ -0,0 +1,43 @@
#include <gdk/gdk.h>
#include <gdk/gdkcolorprivate.h>
#include <gdk/gdkcolorspaceprivate.h>
static void
test_roundtrip_srgb (void)
{
GdkColorSpace *srgb = gdk_color_space_get_srgb ();
GdkColorSpace *srgb_linear = gdk_color_space_get_srgb_linear ();
GdkColor orig;
GdkColor linear;
GdkColor back;
GdkRGBA rgba;
for (int i = 0; i < 1000; i++)
{
rgba.red = g_test_rand_double_range (0., 1.);
rgba.green = g_test_rand_double_range (0., 1.);
rgba.blue = g_test_rand_double_range (0., 1.);
rgba.alpha = g_test_rand_double_range (0., 1.);
gdk_color_init_from_rgba (&orig, &rgba);
gdk_color_convert (&linear, srgb_linear, &orig);
gdk_color_convert (&back, srgb, &linear);
g_assert_true (gdk_color_space_equal (gdk_color_get_color_space (&orig), gdk_color_get_color_space (&back)));
g_assert_cmpfloat_with_epsilon (gdk_color_get_alpha (&orig), gdk_color_get_alpha (&back), 0.0001);
g_assert_cmpfloat_with_epsilon (gdk_color_get_components (&orig)[0], gdk_color_get_components (&back)[0], 0.0001);
g_assert_cmpfloat_with_epsilon (gdk_color_get_components (&orig)[1], gdk_color_get_components (&back)[1], 0.0001);
g_assert_cmpfloat_with_epsilon (gdk_color_get_components (&orig)[2], gdk_color_get_components (&back)[2], 0.0001);
}
}
int
main (int argc, char *argv[])
{
(g_test_init) (&argc, &argv, NULL);
g_test_add_func ("/color/roundtrip-srgb", test_roundtrip_srgb);
return g_test_run ();
}

Some files were not shown because too many files have changed in this diff Show More