Compare commits

...

54 Commits

Author SHA1 Message Date
Matthias Clasen
0806fafbab 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-05-11 15:01:09 -04:00
Matthias Clasen
c58606d116 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-05-11 15:01:09 -04:00
Matthias Clasen
76d0edf64a Add gdk_color_profile_new_from_cicp
This is a bit preliminary, and does not handle hdr
profiles yet.
2022-05-11 15:01:09 -04:00
Matthias Clasen
9e48af467d gsk: Add FIXME
Point out where we should do colorspace conversion
in a shader.
2022-05-11 14:54:53 -04:00
Matthias Clasen
d52552b480 wip: update testsuite for changed compositing 2022-05-10 22:24:26 -04:00
Matthias Clasen
a0c72902f8 gsk: Serialize textures as tiff
This is better than png in that it allows us to
preserve floating point data without going to uin16.
2022-05-10 22:24:26 -04:00
Matthias Clasen
3d9d762998 gsk: Add profile in gsk_renderer_render_texture
Make gsk_renderer_render_texture produce a texture
that has the correct color profile attached. This
fixes the rendering of node files in the node editor.
2022-05-10 09:10:25 -04:00
Matthias Clasen
7a1fc3f259 ci: Squash a compiler warning 2022-05-10 09:10:25 -04:00
Matthias Clasen
e623007759 gsk: Fix a formatting mishap 2022-05-10 09:10:25 -04:00
Matthias Clasen
e9d54b4938 gltexture: Improve the docs a bit 2022-05-10 09:10:25 -04:00
Matthias Clasen
007777d891 colorprofile: fix up the docs a bit 2022-05-10 09:10:25 -04:00
Matthias Clasen
49fc1b5440 gdk: Introduce GdkColor
GdkColor represents a color in the real world, by combining a color
profile, an alpha valueand 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-05-10 09:10:25 -04:00
Matthias Clasen
5877711717 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-05-10 09:10:25 -04:00
Matthias Clasen
5ca041c137 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-05-10 09:10:25 -04:00
Matthias Clasen
1d9af571a5 gsk: Use GL texture information
Use the flags and color profile information from
GL textures to figure out what conversions we need
to do on them.
2022-05-10 09:10:25 -04:00
Matthias Clasen
bf0f7dddc5 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-05-10 09:10:25 -04:00
Matthias Clasen
13cdd37aa6 gsk: Support flipping gl textures
Add upside-down flipping to the list of supported
conversions when handling GL textures.
2022-05-10 09:10:25 -04:00
Matthias Clasen
3d52678635 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 colorspaces are still
done with lcms on the cpu.
2022-05-10 09:10:25 -04:00
Matthias Clasen
6d42ad564a gsk: Refactor a bit 2022-05-10 09:10:25 -04:00
Matthias Clasen
c068bd2452 gsk: Fix caching of textures 2022-05-10 09:10:25 -04:00
Matthias Clasen
3f496dbfd3 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-05-10 09:10:25 -04:00
Matthias Clasen
b46724de0f 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-05-10 09:10:25 -04:00
Matthias Clasen
8fd17568f0 gsk: Linearize gl textures
GL textures are typically in non-linear sRGB,
and in that case, we need to linearize them.
2022-05-10 09:10:25 -04:00
Matthias Clasen
c4a5b72a5f 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-05-10 09:10:25 -04:00
Matthias Clasen
4ada2fe69c gsk: Apply gamma to frames
Use a shader to go from linear sRGB to sRGB when
we are done with a frame.
2022-05-10 09:10:25 -04:00
Matthias Clasen
4ec1fa0a25 gsk: Upload textures in linear sRGB
We want to do compositing in a linear colorspace,
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-05-10 09:10:25 -04:00
Benjamin Otte
06db24b4e1 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-05-10 09:10:25 -04:00
Benjamin Otte
41527b50ab Add crude color management impl for cairo 2022-05-10 09:10:25 -04:00
Matthias Clasen
f8157abd4b Fixes for gdk_memory_txture_new_subtexture
There were several mistakes here.
2022-05-10 09:10:25 -04:00
Benjamin Otte
46a4a31cbc x11: Implement support for color profiles
Stole the implementation from eog.

This doesn't yet update the profile when it changes though.
2022-05-10 09:10:25 -04:00
Matthias Clasen
5750de1e46 Support color profiles in pixbufs
When creating a GdkTexture from a GdkPixbuf,
see if it has an icc profile attached, and if
so, use it.
2022-05-10 09:10:25 -04:00
Benjamin Otte
aef5f3b837 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-05-10 09:10:25 -04:00
Benjamin Otte
6d8f12ae48 colorprofile: 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-05-10 09:10:25 -04:00
Benjamin Otte
46e7509cf7 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-05-10 09:10:25 -04:00
Benjamin Otte
2b5cbf5369 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-05-10 09:10:25 -04:00
Matthias Clasen
73bed6adc3 gsk: Avoid cairo in icon upload
Use the new memory texture magic
2022-05-10 09:10:25 -04:00
Matthias Clasen
c8fce2c35d tiff: Add color profile support
Apply an embedded icc profile when loading tiff
images, and save associated icc profile information
when saving tiff images.
2022-05-10 09:10:25 -04:00
Benjamin Otte
feb6721848 png: Handle color profiles 2022-05-10 09:10:24 -04:00
Matthias Clasen
cd13109f85 gdk: Take a profile in gdk_memory_texture_from_texture
This will let us do profile conversions at the
same time.

Update all callers.
2022-05-10 09:10:24 -04:00
Benjamin Otte
d79c7b14ff gdk: Take a profile in gdk_texture_download_surface
This way, the resulting surface can contain the pixels
in the desired color profile.
2022-05-10 09:10:24 -04:00
Matthias Clasen
007631fe1b gdk: Take a profile in gdk_texture_do_download
Allow specifying a color profile in addition to
a memory format when downloading.
2022-05-10 09:10:24 -04:00
Matthias Clasen
15cce98a31 memoryformat: Take a color profile when converting 2022-05-10 09:10:24 -04:00
Benjamin Otte
7aaaf65d6c API: Add color profile get/set for cairo
Add a centralized place to attach color profiles to.

Nothing uses that information yet, but all the backends do set it.
2022-05-10 09:10:24 -04:00
Benjamin Otte
2c96391bd2 API: Add GdkSurface::color-profile
Unused so far, but there's a private setter for backends and a public
readable property for code.
2022-05-10 09:10:24 -04:00
Matthias Clasen
03ed9592a7 gtk-demo: Add a color profile demo
This is using test images from http://displaycal.net/.
2022-05-10 09:10:24 -04:00
Benjamin Otte
85437edf3d 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-05-10 09:10:24 -04:00
Benjamin Otte
dbc0574d2e jpeg: Add color profile support 2022-05-10 09:10:24 -04:00
Benjamin Otte
5eda3eee87 API: Add gdk_memory_texture_new_with_color_profile()
A version of gdk_memory_texture_new() that allows passing a color
profile. The old version t assumes sRGB.
2022-05-10 09:10:24 -04:00
Benjamin Otte
4ac6fb72fc API: Add a GdkTexture::color-profile property
Returns the associated color profile. For now, this is always sRGB.
2022-05-10 09:10:24 -04:00
Benjamin Otte
7cd6256add API: Add GdkColorProfile
The code doesn't do anything yet, this is just the boilerplate.
2022-05-10 09:10:24 -04:00
Benjamin Otte
4dda8bfb58 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-05-10 09:10:24 -04:00
Matthias Clasen
2e09436483 ci: Update dependencies for msys
Add libpng, libjpeg-turbo, libtiff and lcms2.
2022-05-08 10:32:55 -04:00
Matthias Clasen
b32262128c 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-05-08 10:31:25 -04:00
Benjamin Otte
b867b425a0 cms: Add lcms to the build 2022-05-08 07:06:43 -04:00
104 changed files with 4704 additions and 731 deletions

View File

@@ -25,7 +25,7 @@ variables:
BACKEND_FLAGS: "-Dx11-backend=true -Dwayland-backend=true -Dbroadway-backend=true"
FEATURE_FLAGS: "-Dvulkan=enabled -Dcloudproviders=enabled"
MESON_TEST_TIMEOUT_MULTIPLIER: 3
FEDORA_IMAGE: "registry.gitlab.gnome.org/gnome/gtk/fedora:v36"
FEDORA_IMAGE: "registry.gitlab.gnome.org/gnome/gtk/fedora:v37"
FLATPAK_IMAGE: "registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:master"
.only-default:

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 Profiles
*
* Demonstrates support for color profiles.
*
* 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 ("/colorprofiles/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 ("/colorprofiles/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_colorprofiles (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, "/colorprofiles/colorprofiles.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="/colorprofiles">
<file>colorprofiles.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>colorprofiles.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',
'colorprofiles.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,257 @@
#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;
GdkColorProfile *profile = 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);
profile = gdk_color_profile_new_from_icc_bytes (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);
profile = gdk_color_profile_new_from_cicp (nclx->color_primaries,
nclx->transfer_characteristics,
0,
TRUE,
NULL);
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 (profile == NULL)
profile = g_object_ref (gdk_color_profile_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_profile (width, height,
format, profile,
bytes, stride);
g_bytes_unref (bytes);
g_object_unref (profile);
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,12 @@ else
)
endif
libheif_dep = dependency('libheif')
executable('gtk4-widget-factory',
sources: ['widget-factory.c', widgetfactory_resources],
sources: ['widget-factory.c', 'heif-loader.c', widgetfactory_resources],
c_args: common_cflags,
dependencies: [ libgtk_dep, demo_conf_h ],
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,
@@ -2360,6 +2362,15 @@ 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");
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);
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>
</gresource>
<gresource prefix="/org/gtk/WidgetFactory4">
@@ -118,5 +119,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

@@ -1346,13 +1346,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>
@@ -1361,13 +1521,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>
@@ -1377,12 +1616,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" },
{ "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" },
};

View File

@@ -31,6 +31,7 @@
#include <gdk/gdkcairo.h>
#include <gdk/gdkcairocontext.h>
#include <gdk/gdkclipboard.h>
#include <gdk/gdkcolorprofile.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 "gdkcolorprivate.h"
#include "gdkmemoryformatprivate.h"
#include "gdkcolorprofile.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_profile (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_profile_key;
/**
* gdk_cairo_surface_set_color_profile:
* @surface: a surface
* @profile: the profile to attach to the surface
*
* Attaches a `GdkColorProfile` 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 profile.
*
* The default color profile is assumed to be sRGB, which is not
* linear.
*
* Since: 4.8
*/
void
gdk_cairo_surface_set_color_profile (cairo_surface_t *surface,
GdkColorProfile *profile)
{
g_return_if_fail (surface != NULL);
g_return_if_fail (GDK_IS_COLOR_PROFILE (profile));
cairo_surface_set_user_data (surface,
&color_profile_key,
g_object_ref (profile),
g_object_unref);
}
/**
* gdk_cairo_surface_get_color_profile:
* @surface: a surface
*
* Gets the color profile GTK assumes for the surface. See
* gdk_cairo_surface_set_color_profile() for details.
*
* Returns: (transfer none): the assumed profile
*
* Since: 4.8
*/
GdkColorProfile *
gdk_cairo_surface_get_color_profile (cairo_surface_t *surface)
{
GdkColorProfile *profile;
g_return_val_if_fail (surface != NULL, gdk_color_profile_get_srgb ());
profile = cairo_surface_get_user_data (surface, &color_profile_key);
if (profile == NULL)
profile = gdk_color_profile_get_srgb ();
return profile;
}
/**
* gdk_cairo_get_color_profile:
* @cr: a cairo context
*
* Gets the color profile GTK assumes for the cairo context.
*
* Returns: (transfer none): the assumed profile
*
* Since: 4.8
*/
GdkColorProfile *
gdk_cairo_get_color_profile (cairo_t *cr)
{
GdkColorProfile *profile;
cairo_surface_t *surface;
g_return_val_if_fail (cr != NULL, gdk_color_profile_get_srgb ());
surface = cairo_get_group_target (cr);
profile = cairo_surface_get_user_data (surface, &color_profile_key);
if (profile != NULL)
return profile;
/* theoretically, we should walk the whole group stack, but I don't
* think Cairo lets us do that
*/
surface = cairo_get_target (cr);
profile = cairo_surface_get_user_data (surface, &color_profile_key);
if (profile != NULL)
return profile;
return gdk_color_profile_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_8
void gdk_cairo_surface_set_color_profile (cairo_surface_t *surface,
GdkColorProfile *profile);
GDK_AVAILABLE_IN_4_8
GdkColorProfile * gdk_cairo_surface_get_color_profile (cairo_surface_t *surface);
GDK_AVAILABLE_IN_4_8
GdkColorProfile * gdk_cairo_get_color_profile (cairo_t *cr);
GDK_DEPRECATED_IN_4_6_FOR(gdk_gl_texture_new)
void gdk_cairo_draw_from_gl (cairo_t *cr,

121
gdk/gdkcolor.c Normal file
View File

@@ -0,0 +1,121 @@
/* 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 "gdkcolorprofileprivate.h"
static inline cmsHTRANSFORM *
gdk_color_get_transform (GdkColorProfile *src,
GdkColorProfile *dest)
{
cmsHPROFILE *src_profile, *dest_profile;
src_profile = gdk_color_profile_get_lcms_profile (src);
dest_profile = gdk_color_profile_get_lcms_profile (dest);
return gdk_color_profile_lookup_transform (src,
cmsFormatterForColorspaceOfProfile (src_profile, 4, 1),
dest,
cmsFormatterForColorspaceOfProfile (dest_profile, 4, 1));
}
void
gdk_color_convert (GdkColor *self,
GdkColorProfile *profile,
const GdkColor *other)
{
gdk_color_init (self,
profile,
other->alpha,
NULL,
gdk_color_profile_get_n_components (profile));
cmsDoTransform (gdk_color_get_transform (other->profile, profile),
gdk_color_get_components (other),
(float *) gdk_color_get_components (self),
1);
}
void
gdk_color_convert_rgba (GdkColor *self,
GdkColorProfile *profile,
const GdkRGBA *rgba)
{
gdk_color_init (self,
profile,
rgba->alpha,
NULL,
gdk_color_profile_get_n_components (profile));
cmsDoTransform (gdk_color_get_transform (gdk_color_profile_get_srgb (), profile),
(float[3]) { rgba->red, rgba->green, rgba->blue },
(float *) gdk_color_get_components (self),
1);
}
void
gdk_color_mix (GdkColor *self,
GdkColorProfile *color_profile,
const GdkColor *src1,
const GdkColor *src2,
double progress)
{
if (src1->profile != color_profile)
{
GdkColor tmp;
gdk_color_convert (&tmp, color_profile, src1);
gdk_color_mix (self, color_profile, &tmp, src2, progress);
}
else if (src2->profile != color_profile)
{
GdkColor tmp;
gdk_color_convert (&tmp, color_profile, src2);
gdk_color_mix (self, color_profile, src1, &tmp, progress);
}
else
{
gsize i, n;
const float *s1, *s2;
float *d;
n = gdk_color_profile_get_n_components (color_profile);
gdk_color_init (self,
color_profile,
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/gdkcolorprofile.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
{
GdkColorProfile *profile;
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,
GdkColorProfile *profile,
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,
GdkColorProfile *profile,
const GdkColor *other);
void gdk_color_convert_rgba (GdkColor *self,
GdkColorProfile *profile,
const GdkRGBA *rgba);
void gdk_color_mix (GdkColor *self,
GdkColorProfile *color_profile,
const GdkColor *src1,
const GdkColor *src2,
double progress);
static inline GdkColorProfile * gdk_color_get_color_profile (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->profile) & 1;
}
static inline void
gdk_color_init (GdkColor *self,
GdkColorProfile *profile,
float alpha,
float *components,
gsize n_components)
{
gboolean allocated = n_components > GDK_COLOR_MAX_NATIVE_COMPONENTS;
g_assert (n_components == gdk_color_profile_get_n_components (profile));
self->profile = GSIZE_TO_POINTER (GPOINTER_TO_SIZE (g_object_ref (profile)) | 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_profile_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_profile (self));
}
static inline GdkColorProfile *
gdk_color_get_color_profile (const GdkColor *self)
{
return GSIZE_TO_POINTER (GPOINTER_TO_SIZE (self->profile) & ~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_profile_get_n_components (gdk_color_get_color_profile (self));
}

742
gdk/gdkcolorprofile.c Normal file
View File

@@ -0,0 +1,742 @@
/* gdkcolor_profile.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/>.
*/
/**
* GdkColorProfile:
*
* `GdkColorProfile` is used to describe color spaces.
*
* It is used to associate color profiles defined by the [International
* Color Consortium (ICC)](https://color.org/) with texture and color data.
*
* Each `GdkColorProfile` encapsulates an
* [ICC profile](https://en.wikipedia.org/wiki/ICC_profile). That profile
* can be queried via the [property@Gdk.ColorProfile:icc-profile] property.
*
* GDK provides a predefined color profile for the standard sRGB profile,
* which can be obtained with [func@Gdk.ColorProfile.get_srgb].
*
* The main source for color profiles in GTK is loading images which contain
* ICC profile information. GTK's image loaders will attach the resulting
* color profiles to the textures they produce.
*
* `GdkColorProfile` objects are immutable and therefore threadsafe.
*
* Since: 4.8
*/
#include "config.h"
#include "gdkcolorprofileprivate.h"
#include "gdkintl.h"
struct _GdkColorProfile
{
GObject parent_instance;
GBytes *icc_profile;
cmsHPROFILE lcms_profile;
int color_primaries;
int matrix_coefficients;
int transfer_characteristics;
gboolean full_range;
};
struct _GdkColorProfileClass
{
GObjectClass parent_class;
};
enum {
PROP_0,
PROP_ICC_PROFILE,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
static gboolean
gdk_color_profile_real_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
GdkColorProfile *self = GDK_COLOR_PROFILE (initable);
if (self->lcms_profile == NULL)
{
const guchar *data;
gsize size;
if (self->icc_profile == NULL)
self->icc_profile = g_bytes_new (NULL, 0);
data = g_bytes_get_data (self->icc_profile, &size);
self->lcms_profile = cmsOpenProfileFromMem (data, size);
if (self->lcms_profile == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Failed to load ICC profile"));
return FALSE;
}
}
/* FIXME: can we determine these values from icc */
self->color_primaries = 2;
self->transfer_characteristics = 2;
self->matrix_coefficients = 0;
self->full_range = TRUE;
return TRUE;
}
static void
gdk_color_profile_initable_init (GInitableIface *iface)
{
iface->init = gdk_color_profile_real_init;
}
G_DEFINE_TYPE_WITH_CODE (GdkColorProfile, gdk_color_profile, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gdk_color_profile_initable_init))
static void
gdk_color_profile_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GdkColorProfile *self = GDK_COLOR_PROFILE (gobject);
switch (prop_id)
{
case PROP_ICC_PROFILE:
self->icc_profile = g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
gdk_color_profile_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GdkColorProfile *self = GDK_COLOR_PROFILE (gobject);
switch (prop_id)
{
case PROP_ICC_PROFILE:
g_value_set_boxed (value, self->icc_profile);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
gdk_color_profile_dispose (GObject *object)
{
GdkColorProfile *self = GDK_COLOR_PROFILE (object);
g_clear_pointer (&self->icc_profile, g_bytes_unref);
g_clear_pointer (&self->lcms_profile, cmsCloseProfile);
G_OBJECT_CLASS (gdk_color_profile_parent_class)->dispose (object);
}
static void
gdk_color_profile_class_init (GdkColorProfileClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = gdk_color_profile_set_property;
gobject_class->get_property = gdk_color_profile_get_property;
gobject_class->dispose = gdk_color_profile_dispose;
/**
* GdkColorProfile:icc-profile: (attributes org.gtk.Property.get=gdk_color_profile_get_icc_profile)
*
* the ICC profile for this color profile
*/
properties[PROP_ICC_PROFILE] =
g_param_spec_boxed ("icc-profile",
P_("ICC profile"),
P_("ICC profile for this color profile"),
G_TYPE_BYTES,
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);
}
static void
gdk_color_profile_init (GdkColorProfile *self)
{
}
/**
* gdk_color_profile_get_srgb:
*
* Returns the color profile representing the sRGB color space.
*
* If you don't know anything about color profiles but need one for
* use with some function, this one is most likely the right one.
*
* Returns: (transfer none): the color profile for the sRGB
* color space.
*
* Since: 4.8
*/
GdkColorProfile *
gdk_color_profile_get_srgb (void)
{
static GdkColorProfile *srgb_profile;
if (g_once_init_enter (&srgb_profile))
{
GdkColorProfile *new_profile;
new_profile = gdk_color_profile_new_from_lcms_profile (cmsCreate_sRGBProfile (), NULL);
g_assert (new_profile);
new_profile->color_primaries = 0;
new_profile->transfer_characteristics = 13;
new_profile->matrix_coefficients = 0;
new_profile->full_range = TRUE;
g_once_init_leave (&srgb_profile, new_profile);
}
return srgb_profile;
}
/*<private>
* gdk_color_profile_get_srgb_linear:
*
* Returns the linear color profile corresponding to the sRGB
* color space.
*
* It can display the same colors, but it does not have a gamma curve.
*
* Returns: (transfer none): the color profile for the linear sRGB
* color space.
*
* Since: 4.8
*/
GdkColorProfile *
gdk_color_profile_get_srgb_linear (void)
{
static GdkColorProfile *srgb_linear_profile;
if (g_once_init_enter (&srgb_linear_profile))
{
cmsToneCurve *curve;
cmsHPROFILE lcms_profile;
GdkColorProfile *new_profile;
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);
new_profile = gdk_color_profile_new_from_lcms_profile (lcms_profile, NULL);
g_assert (new_profile);
new_profile->color_primaries = 0;
new_profile->transfer_characteristics = 8;
new_profile->matrix_coefficients = 0;
new_profile->full_range = TRUE;
g_once_init_leave (&srgb_linear_profile, new_profile);
}
return srgb_linear_profile;
}
/**
* gdk_color_profile_new_from_icc_bytes:
* @bytes: 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 `GdkColorProfile` or %NULL on error
*
* Since: 4.8
*/
GdkColorProfile *
gdk_color_profile_new_from_icc_bytes (GBytes *bytes,
GError **error)
{
g_return_val_if_fail (bytes != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_initable_new (GDK_TYPE_COLOR_PROFILE,
NULL,
error,
"icc-profile", bytes,
NULL);
}
GdkColorProfile *
gdk_color_profile_new_from_lcms_profile (cmsHPROFILE lcms_profile,
GError **error)
{
GdkColorProfile *result;
cmsUInt32Number size;
guchar *data;
size = 0;
if (!cmsSaveProfileToMem (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 (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;
}
result = g_object_new (GDK_TYPE_COLOR_PROFILE, NULL);
result->lcms_profile = lcms_profile;
result->icc_profile = g_bytes_new_take (data, size);
return result;
}
/**
* gdk_color_profile_get_icc_profile: (attributes org.gtk.Method.get_property=icc-profile)
* @self: a `GdkColorProfile`
*
* Returns the serialized ICC profile of @self as %GBytes.
*
* Returns: (transfer none): the ICC profile
*
* Since: 4.8
*/
GBytes *
gdk_color_profile_get_icc_profile (GdkColorProfile *self)
{
g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), NULL);
return self->icc_profile;
}
cmsHPROFILE *
gdk_color_profile_get_lcms_profile (GdkColorProfile *self)
{
g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), NULL);
return self->lcms_profile;
}
/**
* gdk_color_profile_is_linear:
* @self: a `GdkColorProfile`
*
* Checks if the given profile is linear.
*
* GTK tries to do compositing in a linear profile.
*
* Some profiles may be linear, but it is not possible to
* determine this easily. In those cases %FALSE will be returned.
*
* Returns: %TRUE if the profile can be proven linear
*
* Since: 4.8
*/
gboolean
gdk_color_profile_is_linear (GdkColorProfile *self)
{
g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), FALSE);
/* FIXME: Make this useful for lcms profiles */
if (self->transfer_characteristics == 8)
return TRUE;
return FALSE;
}
/**
* gdk_color_profile_get_n_components:
* @self: a `GdkColorProfile
*
* Gets the number of color components - also called channels - for
* the given profile.
*
* Note that this does not consider an alpha channel because color
* profiles have no alpha information. So for any form of RGB profile,
* this returned number will be 3, but for CMYK, it will be 4.
*
* Returns: The number of components
*
* Since: 4.8
*/
gsize
gdk_color_profile_get_n_components (GdkColorProfile *self)
{
g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), 3);
return cmsChannelsOf (cmsGetColorSpace (self->lcms_profile));
}
/**
* gdk_color_profile_equal:
* @profile1: (type GdkColorProfile): a `GdkColorProfile`
* @profile2: (type GdkColorProfile): another `GdkColorProfile`
*
* Compares two `GdkColorProfile`s for equality.
*
* Note that this function is not guaranteed to be perfect and two equal
* profiles may compare not equal. However, different profiles will
* never compare equal.
*
* Returns: %TRUE if the two color profiles compare equal
*
* Since: 4.8
*/
gboolean
gdk_color_profile_equal (gconstpointer profile1,
gconstpointer profile2)
{
return profile1 == profile2 ||
g_bytes_equal (GDK_COLOR_PROFILE (profile1)->icc_profile,
GDK_COLOR_PROFILE (profile2)->icc_profile);
}
/* Check if the color profile and the memory format have the
* same color components.
*/
gboolean
gdk_color_profile_supports_memory_format (GdkColorProfile *profile,
GdkMemoryFormat format)
{
/* Currently, all our memory formats are RGB (with or without alpha).
* Update this when that changes.
*/
return cmsGetColorSpace (profile->lcms_profile) == cmsSigRgbData;
}
typedef struct _GdkColorTransformCache GdkColorTransformCache;
struct _GdkColorTransformCache
{
GdkColorProfile *source;
guint source_type;
GdkColorProfile *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_profile_lookup_transform (GdkColorProfile *source,
guint source_type,
GdkColorProfile *dest,
guint dest_type)
{
GdkColorTransformCache *entry;
static GHashTable *cache = NULL;
cmsHTRANSFORM *transform;
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))
{
transform = cmsCreateTransform (gdk_color_profile_get_lcms_profile (source),
source_type,
gdk_color_profile_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;
}
/**
* gdk_color_profile_new_from_cicp:
* @color_primaries: the color primaries to use
* @transfer_characteristics: the transfer function to use
* @matrix_coefficients: the matrix coefficients
* @full_range: whether the data is full-range or not
*
* Creates a new color profile from CICP parameters.
*
* This function only supports a subset of possible combinations
* of @color_primaries and @transfer_characteristics.
*
* @matrix_coefficients and @full_range must have the values
* 0 and `TRUE`, since only full-range RGB profiles are
* supported.
*
* Returns: a new `GdkColorProfile` or %NULL on error
*
* Since: 4.8
*/
GdkColorProfile *
gdk_color_profile_new_from_cicp (int color_primaries,
int transfer_characteristics,
int matrix_coefficients,
gboolean full_range,
GError **error)
{
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_profile_get_srgb ());
else if (transfer_characteristics == 8 /* linear */)
return g_object_ref (gdk_color_profile_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:
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unsupported color primaries (%d)", color_primaries);
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)
{
GdkColorProfile *color_profile;
color_profile = gdk_color_profile_new_from_lcms_profile (profile, error);
color_profile->color_primaries = color_primaries;
color_profile->transfer_characteristics = transfer_characteristics;
color_profile->matrix_coefficients = matrix_coefficients;
color_profile->full_range = full_range;
return color_profile;
}
return NULL;
}
/**
* gdk_color_profile_get_cicp_data:
* @self: a `GdkColorProfile`
* @color_primaries: (out): return location for color primaries
* @transfer_characteristics: (out): return location for transfer characteristics
* @matrix_coefficients: (out): return location for matrix coefficients
* @full_range: (out): return location for the full range flag
*
* Gets the CICP parameters of a color profile.
*
* This is mainly useful for color profiles created via
* [ctor@Gdk.ColorProfile.new_for_cicp].
*
* Note that @color_primaries and @transfer_characteristics will be set to
* 2 (unspecified) if the color profile does not contain CICP information.
*
* Since: 4.8
*/
void
gdk_color_profile_get_cicp_data (GdkColorProfile *self,
int *color_primaries,
int *transfer_characteristics,
int *matrix_coefficients,
gboolean *full_range)
{
g_return_if_fail (GDK_IS_COLOR_PROFILE (self));
*color_primaries = self->color_primaries;
*transfer_characteristics = self->transfer_characteristics;
*matrix_coefficients = self->matrix_coefficients;
*full_range = self->full_range;
}

82
gdk/gdkcolorprofile.h Normal file
View File

@@ -0,0 +1,82 @@
/* gdkcolor_profile.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_PROFILE_H__
#define __GDK_COLOR_PROFILE_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>
G_BEGIN_DECLS
#define GDK_TYPE_COLOR_PROFILE (gdk_color_profile_get_type ())
#define GDK_COLOR_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_COLOR_PROFILE, GdkColorProfile))
#define GDK_IS_COLOR_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_COLOR_PROFILE))
#define GDK_COLOR_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_COLOR_PROFILE, GdkColorProfileClass))
#define GDK_IS_COLOR_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_COLOR_PROFILE))
#define GDK_COLOR_PROFILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_COLOR_PROFILE, GdkColorProfileClass))
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkColorProfile, g_object_unref)
typedef struct _GdkColorProfileClass GdkColorProfileClass;
GDK_AVAILABLE_IN_4_8
GType gdk_color_profile_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_4_8
GdkColorProfile * gdk_color_profile_get_srgb (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_4_8
GdkColorProfile * gdk_color_profile_new_from_icc_bytes (GBytes *bytes,
GError **error);
GDK_AVAILABLE_IN_4_8
GBytes * gdk_color_profile_get_icc_profile (GdkColorProfile *self);
GDK_AVAILABLE_IN_4_8
GdkColorProfile * gdk_color_profile_new_from_cicp (int color_primaries,
int transfer_characteristics,
int matrix_coefficients,
gboolean full_range,
GError **error);
GDK_AVAILABLE_IN_4_8
void gdk_color_profile_get_cicp_data (GdkColorProfile *self,
int *color_primaries,
int *transfer_characteristics,
int *matrix_coefficients,
gboolean *full_range);
GDK_AVAILABLE_IN_4_8
gboolean gdk_color_profile_is_linear (GdkColorProfile *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_8
gsize gdk_color_profile_get_n_components (GdkColorProfile *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_8
gboolean gdk_color_profile_equal (gconstpointer profile1,
gconstpointer profile2);
G_END_DECLS
#endif /* __GDK_COLOR_PROFILE_H__ */

View File

@@ -0,0 +1,30 @@
#ifndef __GDK_COLOR_PROFILE_PRIVATE_H__
#define __GDK_COLOR_PROFILE_PRIVATE_H__
#include "gdkcolorprofile.h"
#include "gdkmemorytexture.h"
#include <lcms2.h>
G_BEGIN_DECLS
GDK_AVAILABLE_IN_ALL
GdkColorProfile * gdk_color_profile_get_srgb_linear (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_ALL
GdkColorProfile * gdk_color_profile_new_from_lcms_profile (cmsHPROFILE lcms_profile,
GError **error);
cmsHPROFILE * gdk_color_profile_get_lcms_profile (GdkColorProfile *self);
gboolean gdk_color_profile_supports_memory_format (GdkColorProfile *profile,
GdkMemoryFormat format);
cmsHTRANSFORM * gdk_color_profile_lookup_transform (GdkColorProfile *source,
guint source_type,
GdkColorProfile *dest,
guint dest_type);
G_END_DECLS
#endif /* __GDK_COLOR_PROFILE_PRIVATE_H__ */

View File

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

View File

@@ -25,6 +25,7 @@
#include "gdkmemoryformatprivate.h"
#include "gdkmemorytextureprivate.h"
#include "gdktextureprivate.h"
#include "gdkcolorprofile.h"
#include <epoxy/gl.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;
GdkColorProfile *profile;
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->profile,
pixels,
texture->width * actual_bpp,
actual_format,
gdk_color_profile_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,
GdkColorProfile *profile,
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, profile, data, stride);
return;
}
download.format = format;
download.profile = profile;
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_profile (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_profile (context, id,
width, height,
GDK_GL_TEXTURE_PREMULTIPLIED,
gdk_color_profile_get_srgb (),
destroy, data);
}
/**
* gdk_gl_texture_new_with_color_profile:
* @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_profile: the `GdkColorProfile` 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 profile
* 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.8
*/
GdkTexture *
gdk_gl_texture_new_with_color_profile (GdkGLContext *context,
guint id,
int width,
int height,
GdkGLTextureFlags flags,
GdkColorProfile *color_profile,
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-profile", color_profile,
NULL);
self->context = g_object_ref (context);
self->id = id;
self->flags = flags;
self->destroy = destroy;
self->data = data;

View File

@@ -49,6 +49,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_8
GdkTexture * gdk_gl_texture_new_with_color_profile (GdkGLContext *context,
guint id,
int width,
int height,
GdkGLTextureFlags flags,
GdkColorProfile *color_profile,
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

View File

@@ -21,6 +21,8 @@
#include "gdkmemoryformatprivate.h"
#include "gdkcolorprofileprivate.h"
#include "gdkprofilerprivate.h"
#include "gsk/gl/fp16private.h"
#include <epoxy/gl.h>
@@ -72,6 +74,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 +385,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 +401,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 +419,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 +428,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 +442,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 +456,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 +470,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 +484,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 +498,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 +512,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 +526,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 +540,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 +554,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 +568,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 +582,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 +596,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 +610,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 +624,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 +638,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 +652,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 +666,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 +759,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,
GdkColorProfile *dest_profile,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
GdkColorProfile *src_profile,
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_profile_lookup_transform (src_profile,
src_desc->lcms.type,
dest_profile,
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,
GdkColorProfile *dest_profile,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
GdkColorProfile *src_profile,
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_profile_equal (src_profile, dest_profile);
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_profile,
src_data, src_stride, src_format, src_profile,
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 "gdkcolorprofile.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,
GdkColorProfile *dest_profile,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
GdkColorProfile *src_profile,
gsize width,
gsize height);

View File

@@ -21,6 +21,7 @@
#include "gdkmemorytextureprivate.h"
#include "gdkcolorprofileprivate.h"
#include "gdkmemoryformatprivate.h"
#include "gsk/gl/fp16private.h"
@@ -58,6 +59,7 @@ gdk_memory_texture_dispose (GObject *object)
static void
gdk_memory_texture_download (GdkTexture *texture,
GdkMemoryFormat format,
GdkColorProfile *profile,
guchar *data,
gsize stride)
{
@@ -65,9 +67,11 @@ gdk_memory_texture_download (GdkTexture *texture,
gdk_memory_convert (data, stride,
format,
profile,
(guchar *) g_bytes_get_data (self->bytes, NULL),
self->stride,
texture->format,
gdk_texture_get_color_profile (texture),
gdk_texture_get_width (texture),
gdk_texture_get_height (texture));
}
@@ -136,6 +140,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_profile]
* with the sRGB profile.
*
* Returns: (type GdkMemoryTexture): A newly-created `GdkTexture`
*/
GdkTexture *
@@ -145,18 +152,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_profile (width, height,
format,
gdk_color_profile_get_srgb (),
bytes, stride);
}
/**
* gdk_memory_texture_new_with_color_profile:
* @width: the width of the texture
* @height: the height of the texture
* @format: the format of the data
* @profile: a `GdkColorProfile`
* @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 profile.
*
* The `GBytes` must contain @stride x @height pixels
* in the given format.
*
* Returns: A newly-created `GdkTexture`
*
* Since: 4.8
*/
GdkTexture *
gdk_memory_texture_new_with_color_profile (int width,
int height,
GdkMemoryFormat format,
GdkColorProfile *profile,
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_PROFILE (profile), 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_profile_supports_memory_format (profile, 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-profile", profile,
NULL);
GDK_TEXTURE (self)->format = format;
@@ -178,15 +225,15 @@ gdk_memory_texture_new_subtexture (GdkMemoryTexture *source,
GBytes *bytes;
g_return_val_if_fail (GDK_IS_MEMORY_TEXTURE (source), NULL);
g_return_val_if_fail (x < 0 || x >= GDK_TEXTURE (source)->width, NULL);
g_return_val_if_fail (y < 0 || y >= GDK_TEXTURE (source)->height, NULL);
g_return_val_if_fail (width <= 0 || x + width > GDK_TEXTURE (source)->width, NULL);
g_return_val_if_fail (height <= 0 || y + height > GDK_TEXTURE (source)->height, NULL);
g_return_val_if_fail (x >= 0 || x < GDK_TEXTURE (source)->width, NULL);
g_return_val_if_fail (y >= 0 || y < GDK_TEXTURE (source)->height, NULL);
g_return_val_if_fail (width > 0 || x + width <= GDK_TEXTURE (source)->width, NULL);
g_return_val_if_fail (height > 0 || y + height <= GDK_TEXTURE (source)->height, NULL);
texture = GDK_TEXTURE (source);
bpp = gdk_memory_format_bytes_per_pixel (texture->format);
offset = y * source->stride + x * bpp;
size = source->stride * (height - 1) + x * bpp;
size = source->stride * (height - 1) + width * bpp;
bytes = g_bytes_new_from_bytes (source->bytes, offset, size);
result = gdk_memory_texture_new (texture->width,
@@ -201,7 +248,8 @@ gdk_memory_texture_new_subtexture (GdkMemoryTexture *source,
GdkMemoryTexture *
gdk_memory_texture_from_texture (GdkTexture *texture,
GdkMemoryFormat format)
GdkMemoryFormat format,
GdkColorProfile *profile)
{
GdkTexture *result;
GBytes *bytes;
@@ -214,20 +262,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_profile (texture) == profile)
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, profile, 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_profile (texture->width,
texture->height,
format,
profile,
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/gdkcolorprofile.h>
G_BEGIN_DECLS
@@ -68,6 +69,14 @@ GdkTexture * gdk_memory_texture_new (int
GdkMemoryFormat format,
GBytes *bytes,
gsize stride);
GDK_AVAILABLE_IN_4_8
GdkTexture * gdk_memory_texture_new_with_color_profile
(int width,
int height,
GdkMemoryFormat format,
GdkColorProfile *profile,
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,
GdkColorProfile *profile);
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_profile_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 "gdk-private.h"
#include "gdkcairo.h"
#include "gdkcolorprofile.h"
#include "gdkcontentprovider.h"
#include "gdkdeviceprivate.h"
#include "gdkdisplayprivate.h"
@@ -88,13 +90,14 @@ enum {
enum {
PROP_0,
PROP_COLOR_PROFILE,
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_profile = g_object_ref (gdk_color_profile_get_srgb ());
surface->device_cursor = g_hash_table_new_full (NULL, NULL,
NULL, g_object_unref);
}
@@ -500,6 +505,26 @@ gdk_surface_class_init (GdkSurfaceClass *klass)
klass->beep = gdk_surface_real_beep;
/**
* GdkSurface:color-profile: (attributes org.gtk.Property.get=gdk_surface_get_color_profile)
*
* The preferred color profile for rendering to the surface
*
* This profile is negotiated between GTK and the compositor.
*
* The profile 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 show, the
* profile may not represent the actual color profile that is going to be used.
*
* Since: 4.8
*/
properties[PROP_COLOR_PROFILE] =
g_param_spec_object ("color-profile",
P_("Color profile"),
P_("The preferred color profile of the surface"),
GDK_TYPE_COLOR_PROFILE,
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)
*
@@ -732,6 +757,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_profile);
g_clear_object (&surface->display);
@@ -786,6 +812,10 @@ gdk_surface_get_property (GObject *object,
switch (prop_id)
{
case PROP_COLOR_PROFILE:
g_value_set_object (value, gdk_surface_get_color_profile (surface));
break;
case PROP_CURSOR:
g_value_set_object (value, gdk_surface_get_cursor (surface));
break;
@@ -2054,6 +2084,40 @@ gdk_surface_get_height (GdkSurface *surface)
return surface->height;
}
void
gdk_surface_set_color_profile (GdkSurface *self,
GdkColorProfile *color_profile)
{
/* This way we support unsetting, too */
if (GDK_DISPLAY_DEBUG_CHECK (self->display, SRGB))
color_profile = gdk_color_profile_get_srgb();
if (gdk_color_profile_equal (self->color_profile, color_profile))
return;
g_set_object (&self->color_profile, color_profile);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_COLOR_PROFILE]);
}
/**
* gdk_surface_get_color_profile: (attributes org.gtk.Method.get_property=color-profile)
* @self: a `GdkSurface`
*
* Returns the preferred color profile for rendering to the given @surface.
*
* Returns: (transfer none): The color profile of @surface
*
* Since: 4.8
*/
GdkColorProfile *
gdk_surface_get_color_profile (GdkSurface *self)
{
g_return_val_if_fail (GDK_IS_SURFACE (self), gdk_color_profile_get_srgb ());
return self->color_profile;
}
/*
* gdk_surface_get_origin:
* @surface: a `GdkSurface`
@@ -2374,7 +2438,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)
@@ -2390,6 +2454,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_profile (similar_surface, gdk_surface_get_color_profile (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_8
GdkColorProfile * gdk_surface_get_color_profile (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;
GdkColorProfile *color_profile;
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_profile (GdkSurface *self,
GdkColorProfile *color_profile);
GdkMonitor * gdk_surface_get_layout_monitor (GdkSurface *surface,
GdkPopupLayout *layout,

View File

@@ -39,7 +39,10 @@
#include "gdktextureprivate.h"
#include "gdkcairo.h"
#include "gdkcolorprofile.h"
#include "gdkintl.h"
#include "gdkmemoryformatprivate.h"
#include "gdkmemorytextureprivate.h"
#include "gdkpaintable.h"
#include "gdksnapshot.h"
@@ -59,8 +62,9 @@ gtk_snapshot_append_texture (GdkSnapshot *snapshot,
enum {
PROP_0,
PROP_WIDTH,
PROP_COLOR_PROFILE,
PROP_HEIGHT,
PROP_WIDTH,
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,
GdkColorProfile *profile,
guchar *data,
gsize stride)
{
@@ -239,14 +244,20 @@ gdk_texture_set_property (GObject *gobject,
switch (prop_id)
{
case PROP_WIDTH:
self->width = g_value_get_int (value);
case PROP_COLOR_PROFILE:
self->color_profile = g_value_dup_object (value);
if (self->color_profile == NULL)
self->color_profile = g_object_ref (gdk_color_profile_get_srgb ());
break;
case PROP_HEIGHT:
self->height = g_value_get_int (value);
break;
case PROP_WIDTH:
self->width = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
@@ -263,14 +274,18 @@ gdk_texture_get_property (GObject *gobject,
switch (prop_id)
{
case PROP_WIDTH:
g_value_set_int (value, self->width);
case PROP_COLOR_PROFILE:
g_value_set_object (value, self->color_profile);
break;
case PROP_HEIGHT:
g_value_set_int (value, self->height);
break;
case PROP_WIDTH:
g_value_set_int (value, self->width);
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_profile);
G_OBJECT_CLASS (gdk_texture_parent_class)->dispose (object);
}
@@ -299,14 +315,31 @@ gdk_texture_class_init (GdkTextureClass *klass)
gobject_class->dispose = gdk_texture_dispose;
/**
* GdkTexture:width: (attributes org.gtk.Property.get=gdk_texture_get_width)
* GdkTexture:color-profile: (attributes org.gtk.Property.get=gdk_texture_get_color_profile)
*
* The width of the texture, in pixels.
* The color profile associated with texture.
*
* Since: 4.8
*/
properties[PROP_WIDTH] =
g_param_spec_int ("width",
"Width",
"The width of the texture",
properties[PROP_COLOR_PROFILE] =
g_param_spec_object ("color-profile",
P_("Color Profile"),
P_("The associated color profile"),
GDK_TYPE_COLOR_PROFILE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS |
G_PARAM_EXPLICIT_NOTIFY);
/**
* GdkTexture:height: (attributes org.gtk.Property.get=gdk_texture_get_height)
*
* The height of the texture, in pixels.
*/
properties[PROP_HEIGHT] =
g_param_spec_int ("height",
P_("Height"),
P_("The height of the texture"),
1,
G_MAXINT,
1,
@@ -316,14 +349,14 @@ gdk_texture_class_init (GdkTextureClass *klass)
G_PARAM_EXPLICIT_NOTIFY);
/**
* GdkTexture:height: (attributes org.gtk.Property.get=gdk_texture_get_height)
* GdkTexture:width: (attributes org.gtk.Property.get=gdk_texture_get_width)
*
* The height of the texture, in pixels.
* The width of the texture, in pixels.
*/
properties[PROP_HEIGHT] =
g_param_spec_int ("height",
"Height",
"The height of the texture",
properties[PROP_WIDTH] =
g_param_spec_int ("width",
P_("Width"),
P_("The width of the texture"),
1,
G_MAXINT,
1,
@@ -365,18 +398,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_profile (cairo_image_surface_get_width (surface),
cairo_image_surface_get_height (surface),
GDK_MEMORY_DEFAULT,
gdk_cairo_surface_get_color_profile (surface),
bytes,
cairo_image_surface_get_stride (surface));
g_bytes_unref (bytes);
return texture;
}
static GdkColorProfile *
gdk_color_profile_from_pixbuf (GdkPixbuf *pixbuf)
{
const char *icc_profile_base64;
GdkColorProfile *profile = 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);
profile = gdk_color_profile_new_from_icc_bytes (bytes, NULL);
g_bytes_unref (bytes);
}
if (!profile)
profile = g_object_ref (gdk_color_profile_get_srgb ());
return profile;
}
/**
* gdk_texture_new_for_pixbuf:
* @pixbuf: a `GdkPixbuf`
@@ -394,24 +452,31 @@ gdk_texture_new_for_pixbuf (GdkPixbuf *pixbuf)
{
GdkTexture *texture;
GBytes *bytes;
GdkColorProfile *profile;
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
profile = gdk_color_profile_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_profile (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,
profile,
bytes,
gdk_pixbuf_get_rowstride (pixbuf));
g_bytes_unref (bytes);
g_object_unref (profile);
return texture;
}
@@ -669,32 +734,73 @@ gdk_texture_get_height (GdkTexture *texture)
return texture->height;
}
/**
* gdk_texture_get_color_profile: (attributes org.gtk.Method.get_property=color-profile)
* @texture: a `GdkTexture`
*
* Returns the color profile associsated with @texture.
*
* Returns: (transfer none): the color profile of the `GdkTexture`
*
* Since: 4.8
*/
GdkColorProfile *
gdk_texture_get_color_profile (GdkTexture *texture)
{
g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
return texture->color_profile;
}
void
gdk_texture_do_download (GdkTexture *texture,
GdkMemoryFormat format,
GdkColorProfile *profile,
guchar *data,
gsize stride)
{
GDK_TEXTURE_GET_CLASS (texture)->download (texture, format, data,stride);
GDK_TEXTURE_GET_CLASS (texture)->download (texture, format, profile, data, stride);
}
/*<private>
* gdk_texture_download_surface:
* @texture: the texture to download
* @profile: (nullable): The target color profile 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,
GdkColorProfile *profile)
{
cairo_surface_t *surface;
cairo_status_t surface_status;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
texture->width, texture->height);
if (profile != NULL)
gdk_cairo_surface_set_color_profile (surface, profile);
else
profile = gdk_color_profile_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,
profile,
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;
@@ -738,6 +844,7 @@ gdk_texture_download (GdkTexture *texture,
gdk_texture_do_download (texture,
GDK_MEMORY_DEFAULT,
gdk_color_profile_get_srgb (),
data,
stride);
}
@@ -755,7 +862,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

@@ -84,6 +84,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_8
GdkColorProfile * gdk_texture_get_color_profile (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;
GdkColorProfile *color_profile;
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,
GdkColorProfile *profile,
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,
GdkColorProfile *profile);
void gdk_texture_do_download (GdkTexture *texture,
GdkMemoryFormat format,
GdkColorProfile *profile,
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 _GdkColorProfile GdkColorProfile;
typedef struct _GdkContentFormats GdkContentFormats;
typedef struct _GdkContentProvider GdkContentProvider;
typedef struct _GdkCursor GdkCursor;

View File

@@ -21,9 +21,10 @@
#include "gdkjpegprivate.h"
#include "gdkcolorprofile.h"
#include "gdkintl.h"
#include "gdktexture.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;
GdkColorProfile *color_profile;
G_GNUC_UNUSED guint64 before = GDK_PROFILER_CURRENT_TIME;
info.err = jpeg_std_error (&jerr.pub);
@@ -165,6 +169,9 @@ gdk_load_jpeg (GBytes *input_bytes,
g_bytes_get_data (input_bytes, NULL),
g_bytes_get_size (input_bytes));
/* save color profile */
jpeg_save_markers (&info, JPEG_APP0 + 2, 0xFFFF);
jpeg_read_header (&info, TRUE);
jpeg_start_decompress (&info);
@@ -222,19 +229,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_profile = gdk_color_profile_new_from_icc_bytes (icc_bytes, error);
g_bytes_unref (icc_bytes);
}
else
color_profile = g_object_ref (gdk_color_profile_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_profile)
{
texture = gdk_memory_texture_new_with_color_profile (width, height,
format,
color_profile,
bytes, stride);
}
else
texture = NULL;
g_bytes_unref (bytes);
gdk_profiler_end_mark (before, "jpeg load", NULL);
return texture;
}
@@ -252,9 +274,12 @@ gdk_save_jpeg (GdkTexture *texture)
gsize texstride;
guchar *row;
int width, height;
GdkColorProfile *color_profile;
GBytes *bytes;
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
color_profile = gdk_texture_get_color_profile (texture);
info.err = jpeg_std_error (&jerr.pub);
jerr.pub.error_exit = fatal_error_handler;
@@ -283,12 +308,19 @@ 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_profile_get_srgb ());
texdata = gdk_memory_texture_get_data (memtex);
texstride = gdk_memory_texture_get_stride (memtex);
jpeg_start_compress (&info, TRUE);
bytes = gdk_color_profile_get_icc_profile (color_profile);
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

@@ -19,6 +19,7 @@
#include "gdkpngprivate.h"
#include "gdkcolorprofileprivate.h"
#include "gdkintl.h"
#include "gdkmemoryformatprivate.h"
#include "gdkmemorytextureprivate.h"
@@ -127,8 +128,102 @@ png_simple_warning_callback (png_structp png,
{
}
/* }}} */
/* {{{ Public API */
static GdkColorProfile *
gdk_png_get_color_profile (png_struct *png,
png_info *info)
{
GdkColorProfile *profile;
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);
profile = gdk_color_profile_new_from_icc_bytes (bytes, NULL);
g_bytes_unref (bytes);
if (profile)
return profile;
}
if (png_get_sRGB (png, info, &intent))
return g_object_ref (gdk_color_profile_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_profile_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_profile_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 });
profile = gdk_color_profile_new_from_lcms_profile (lcms_profile, NULL);
/* FIXME: errors? */
if (profile == NULL)
profile = g_object_ref (gdk_color_profile_get_srgb ());
cmsFreeToneCurve (curve);
return profile;
}
static void
gdk_png_set_color_profile (png_struct *png,
png_info *info,
GdkColorProfile *profile)
{
/* FIXME: allow deconstructing RGB profiles into gAMA and cHRM instead of
* falling back to iCCP */
if (profile == gdk_color_profile_get_srgb ())
{
png_set_sRGB_gAMA_and_cHRM (png, info, /* FIXME */ PNG_sRGB_INTENT_PERCEPTUAL);
}
else
{
GBytes *bytes = gdk_color_profile_get_icc_profile (profile);
png_set_iCCP (png, info,
"ICC profile",
0,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes));
}
}
/* }}} */
/* {{{ Public API */
GdkTexture *
gdk_load_png (GBytes *bytes,
@@ -144,6 +239,7 @@ gdk_load_png (GBytes *bytes,
guchar *buffer = NULL;
guchar **row_pointers = NULL;
GBytes *out_bytes;
GdkColorProfile *color_profile;
GdkTexture *texture;
int bpp;
G_GNUC_UNUSED gint64 before = GDK_PROFILER_CURRENT_TIME;
@@ -247,6 +343,8 @@ gdk_load_png (GBytes *bytes,
return NULL;
}
color_profile = gdk_png_get_color_profile (png, info);
bpp = gdk_memory_format_bytes_per_pixel (format);
stride = width * bpp;
if (stride % 8)
@@ -257,6 +355,7 @@ gdk_load_png (GBytes *bytes,
if (!buffer || !row_pointers)
{
g_object_unref (color_profile);
g_free (buffer);
g_free (row_pointers);
png_destroy_read_struct (&png, &info, NULL);
@@ -273,8 +372,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_profile (width, height,
format, color_profile,
out_bytes, stride);
g_bytes_unref (out_bytes);
g_object_unref (color_profile);
g_free (row_pointers);
png_destroy_read_struct (&png, &info, NULL);
@@ -301,13 +403,17 @@ gdk_save_png (GdkTexture *texture)
int y;
GdkMemoryTexture *memtex;
GdkMemoryFormat format;
GdkColorProfile *color_profile;
int png_format;
int depth;
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
color_profile = gdk_texture_get_color_profile (texture);
format = gdk_texture_get_format (texture);
memtex = gdk_memory_texture_from_texture (texture, format, color_profile);
switch (format)
{
case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
@@ -317,14 +423,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;
@@ -369,8 +483,6 @@ gdk_save_png (GdkTexture *texture)
return NULL;
}
memtex = gdk_memory_texture_from_texture (texture, format);
if (sigsetjmp (png_jmpbuf (png), 1))
{
g_object_unref (memtex);
@@ -387,6 +499,8 @@ gdk_save_png (GdkTexture *texture)
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
gdk_png_set_color_profile (png, info, color_profile);
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 GdkColorProfile *
gdk_tiff_get_color_profile (TIFF *tiff)
{
const char *icc_data;
guint icc_len;
if (TIFFGetField (tiff, TIFFTAG_ICCPROFILE, &icc_len, &icc_data))
{
GBytes *icc_bytes;
GdkColorProfile *profile;
icc_bytes = g_bytes_new (icc_data, icc_len);
profile = gdk_color_profile_new_from_icc_bytes (icc_bytes, NULL);
g_bytes_unref (icc_bytes);
if (profile)
return profile;
}
return g_object_ref (gdk_color_profile_get_srgb ());
}
static void
gdk_tiff_set_color_profile (TIFF *tiff,
GdkColorProfile *profile)
{
GBytes *bytes = gdk_color_profile_get_icc_profile (profile);
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;
GdkColorProfile *color_profile;
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_profile = gdk_texture_get_color_profile (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_profile (tif, color_profile);
memtex = gdk_memory_texture_from_texture (texture, fdata->format, color_profile);
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;
GdkColorProfile *profile;
GdkTexture *texture;
TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &width);
@@ -342,12 +385,15 @@ load_fallback (TIFF *tif,
return NULL;
}
profile = gdk_tiff_get_color_profile (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_profile (width, height,
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
profile,
bytes,
width * 4);
g_bytes_unref (bytes);
@@ -372,6 +418,7 @@ gdk_load_tiff (GBytes *input_bytes,
gsize stride;
int bpp;
GBytes *bytes;
GdkColorProfile *profile;
GdkTexture *texture;
G_GNUC_UNUSED gint64 before = GDK_PROFILER_CURRENT_TIME;
@@ -471,12 +518,14 @@ gdk_load_tiff (GBytes *input_bytes,
line += stride;
}
profile = gdk_tiff_get_color_profile (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_profile (width, height,
format, profile,
bytes, width * bpp);
g_bytes_unref (bytes);
TIFFClose (tif);

View File

@@ -4,6 +4,8 @@ gdk_public_sources = files([
'gdkcairo.c',
'gdkcairocontext.c',
'gdkclipboard.c',
'gdkcolor.c',
'gdkcolorprofile.c',
'gdkcontentdeserializer.c',
'gdkcontentformats.c',
'gdkcontentprovider.c',
@@ -64,6 +66,7 @@ gdk_public_headers = files([
'gdkcairo.h',
'gdkcairocontext.h',
'gdkclipboard.h',
'gdkcolorprofile.h',
'gdkcontentdeserializer.h',
'gdkcontentformats.h',
'gdkcontentprovider.h',
@@ -205,6 +208,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_profile (self->paint_surface,
gdk_surface_get_color_profile (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_profile (cairo_surface, gdk_surface_get_color_profile (surface));
return cairo_surface;
}

View File

@@ -49,6 +49,8 @@ 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_profile (cairo_surface,
gdk_surface_get_color_profile (surface));
return cairo_surface;
}

View File

@@ -1403,6 +1403,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_profile (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_profile);
self->color_profile = g_object_ref (gdk_color_profile_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_profile);
self->color_profile = gdk_color_profile_new_from_icc_bytes (bytes, NULL);
if (!self->color_profile)
self->color_profile = g_object_ref (gdk_color_profile_get_srgb ());
}
/**
* gdk_x11_display_open:
* @display_name: (nullable): name of the X display.
@@ -1470,6 +1535,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_profile (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_profile);
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 */
GdkColorProfile *color_profile;
/* 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_profile (surface, GDK_X11_DISPLAY (display)->color_profile);
if (!gdk_running_in_sandbox ())
{
@@ -3180,7 +3182,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/gdkcolorprofileprivate.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;
GdkColorProfile *data_profile;
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_profile = gdk_texture_get_color_profile (texture);
if (data_profile == gdk_color_profile_get_srgb ())
*conversion = GSK_CONVERSION_LINEARIZE;
else if (data_profile == gdk_color_profile_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_profile_get_srgb_linear ());
*conversion = 0;
}
else
{
memtex = gdk_memory_texture_from_texture (texture, gdk_texture_get_format (texture), gdk_texture_get_color_profile (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/gdkcolorprofileprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdktextureprivate.h>
@@ -684,6 +686,140 @@ 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;
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 = gsk_gl_command_queue_add_vertices (command_queue);
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`
@@ -714,20 +850,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))
{
@@ -736,35 +881,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;
}
GdkColorProfile *profile;
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);
profile = gdk_texture_get_color_profile (texture);
flags = gdk_gl_texture_get_flags (gl_texture);
/* A GL texture from the same GL context is a simple task... */
if (profile == gdk_color_profile_get_srgb_linear () &&
flags == GDK_GL_TEXTURE_PREMULTIPLIED)
{
return gl_texture_id;
}
else if (profile == gdk_color_profile_get_srgb () ||
profile == gdk_color_profile_get_srgb_linear ())
{
conversion = 0;
if (profile == gdk_color_profile_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_profile (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,
@@ -772,6 +944,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;
@@ -830,6 +1011,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;
}
@@ -1197,6 +1380,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;
@@ -1214,6 +1398,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;
@@ -1227,7 +1414,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_profile (texture));
for (guint col = 0; col < cols; col ++)
{
@@ -1239,13 +1427,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;
@@ -1331,8 +1544,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,
GdkColorProfile *profile)
{
GskGLTextureState *state;
GskGLTexture *texture;
@@ -1360,10 +1575,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_profile (self->command_queue->context,
texture_id,
width,
height,
flags,
profile,
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,
GdkColorProfile *profile);
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/gdkcolorprofileprivate.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,
@@ -284,9 +309,11 @@ gsk_gl_glyph_library_upload_glyph (GskGLGlyphLibrary *self,
gdk_memory_convert (pixel_data,
width * 4,
GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
gdk_color_profile_get_srgb_linear (),
cairo_image_surface_get_data (surface),
width * 4,
GDK_MEMORY_DEFAULT,
gdk_color_profile_get_srgb_linear (),
width, height);
gl_format = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;

View File

@@ -23,7 +23,9 @@
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdkmemoryformatprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdkcolorprofileprivate.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;
GdkColorProfile *profile;
GdkMemoryFormat format;
format = gdk_texture_get_format (texture);
profile = gdk_texture_get_color_profile (texture);
memtex = gdk_memory_texture_from_texture (texture, format, profile);
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_profile_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

@@ -24,6 +24,7 @@
#include <gdk/gdkdisplayprivate.h>
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdksurfaceprivate.h>
#include <gdk/gdkcolorprofileprivate.h>
#include <gdk/gdkintl.h>
#include <gsk/gskdebugprivate.h>
#include <gsk/gskrendererprivate.h>
@@ -386,7 +387,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_profile_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/gdkcolorprofileprivate.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_profile_get_srgb_linear (),
(guchar *) rgba,
sizeof (float) * 4,
GDK_MEMORY_R32G32B32A32_FLOAT,
gdk_color_profile_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_profile (rendered_surface, gdk_color_profile_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/gdkcolorprofileprivate.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_profile (surface, gdk_color_profile_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_profile_is_linear (gdk_cairo_get_color_profile (cr)))
{
gsk_cairo_renderer_do_render (renderer, cr, root);
}
else
{
GdkSurface *surface = gsk_renderer_get_surface (renderer);
GdkColorProfile *target_profile = gdk_cairo_get_color_profile (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_profile (cairo_surface,
gdk_color_profile_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_profile);
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/gdk-private.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;
GdkColorProfile *profile;
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);
profile = gdk_cairo_get_color_profile (cr);
for (i = 0; i < self->n_stops; i++)
{
GdkColor color;
const float *components;
gdk_color_convert_rgba (&color, profile, &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;
GdkColorProfile *profile;
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);
profile = gdk_cairo_get_color_profile (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, profile, &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;
GdkColorProfile *profile;
cairo_pattern_t *pattern;
graphene_point_t corner;
float radius;
gsize i;
profile = gdk_cairo_get_color_profile (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, profile, &stop1->color);
gdk_color_convert_rgba (&stop2_color, profile, &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,
profile,
&stop1_color,
&stop2_color,
(start_angle - offset1) / (offset2 - offset1));
gdk_color_mix (&end_color,
profile,
&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,
GdkColorProfile *profile,
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, profile, 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.
*/
GdkColorProfile *profile;
cairo_pattern_t *mesh;
cairo_matrix_t mat;
graphene_point_t tl, br;
float scale;
profile = gdk_cairo_get_color_profile (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,
profile,
&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,
profile,
&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,
profile,
&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,
profile,
&self->border_color[3],
0, 0,
tl.x, tl.y,
@@ -1493,7 +1526,8 @@ 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_profile (cr));
pattern = cairo_pattern_create_for_surface (surface);
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
@@ -4428,12 +4462,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 +4490,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

@@ -1102,7 +1102,7 @@ parse_conic_gradient_node (GtkCssParser *parser)
g_array_append_val (stops, to);
}
result = gsk_conic_gradient_node_new (&bounds, &center, rotation,
result = gsk_conic_gradient_node_new (&bounds, &center, rotation,
(GskColorStop *) stops->data, stops->len);
g_array_free (stops, TRUE);
@@ -1388,7 +1388,7 @@ parse_cairo_node (GtkCssParser *parser)
parse_declarations (parser, declarations, G_N_ELEMENTS(declarations));
node = gsk_cairo_node_new (&bounds);
if (surface != NULL)
{
cairo_t *cr = gsk_cairo_node_get_draw_context (node);
@@ -1399,7 +1399,8 @@ 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_profile (pixels));
cairo_set_source_surface (cr, surface, 0, 0);
cairo_paint (cr);
cairo_destroy (cr);
@@ -2697,10 +2698,10 @@ render_node_print (Printer *p,
start_node (p, "texture");
append_rect_param (p, "bounds", &node->bounds);
bytes = gdk_texture_save_to_png_bytes (texture);
bytes = gdk_texture_save_to_tiff_bytes (texture);
_indent (p);
g_string_append (p->str, "texture: url(\"data:image/png;base64,");
g_string_append (p->str, "texture: url(\"data:image/tiff;base64,");
b64 = base64_encode_with_linebreaks (g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes));
append_escaping_newlines (p->str, b64);

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_profile (priv->context,
texture->id,
texture->width,
texture->height,
GDK_GL_TEXTURE_PREMULTIPLIED|GDK_GL_TEXTURE_FLIPPED,
gdk_color_profile_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

@@ -16,6 +16,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.23'
wayland_req = '>= 1.20.0'
graphene_req = '>= 1.9.1'
@@ -393,6 +394,8 @@ pango_dep = dependency('pango', version: pango_req,
fallback : ['pango', 'libpango_dep'])
fribidi_dep = dependency('fribidi', version: fribidi_req,
fallback : ['fribidi', 'libfribidi_dep'])
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

View File

@@ -291,17 +291,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_profile (self->gdk_context,
*(guint *) frame->data[0],
frame->info.width,
frame->info.height,
0, /* Neither premultiplied nor flipped */
gdk_color_profile_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

@@ -148,3 +148,12 @@ if libsysprof_dep.found()
dependencies: [libsysprof_dep, platform_gio_dep, libm],
)
endif
libheif_dep = dependency('libheif')
if libheif_dep.found()
executable('testheif',
sources: 'testheif.c',
dependencies: [libgtk_dep, libheif_dep],
)
endif

290
tests/testheif.c Normal file
View File

@@ -0,0 +1,290 @@
#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;
GdkColorProfile *profile = 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);
profile = gdk_color_profile_new_from_icc_bytes (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);
profile = gdk_color_profile_new_from_cicp (nclx->color_primaries,
nclx->transfer_characteristics,
0,
TRUE,
NULL);
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 (profile == NULL)
profile = g_object_ref (gdk_color_profile_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_profile (width, height,
format, profile,
bytes, stride);
g_bytes_unref (bytes);
g_object_unref (profile);
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);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 507 B

After

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 B

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 B

After

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

After

Width:  |  Height:  |  Size: 761 B

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