Compare commits

...

68 Commits

Author SHA1 Message Date
Matthias Clasen
4b2c9130a9 Add some assertions 2021-10-06 02:19:43 -04:00
Matthias Clasen
27361bf136 Split GdkColorProfile
Introduce GdkICCProfile and GdkDerivedProfile subclasses.
GdkICCProfile takes over the ICC profile functionality
from GdkColorProfile. A GdkDerivedProfile is our way
of encoding coordinate transformations on top of ICC
profiles, such as HSV or Lch. Update the memory conversion
code to only handle ICC profiles, and update the color
conversion code to accept derived profiles as well as
ICC profiles.
2021-10-06 02:17:07 -04:00
Matthias Clasen
be2aaae7a9 media: Use the new GL texture constructor
Use GdkGLTextureFlags in the gstreamer backend
to tell gsk that the buffers are unpremultiplied,
non-linear sRGB.
2021-10-06 02:16:44 -04:00
Matthias Clasen
be17bf29e1 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.
2021-10-06 02:16:44 -04:00
Matthias Clasen
3ef4e0b16c ngl: 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.
2021-10-06 02:16:43 -04:00
Matthias Clasen
2dc0c6b380 gdk: 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.
2021-10-06 02:16:43 -04:00
Matthias Clasen
7bb503128e ngl: Support flipping gl textures
Add upside-down flipping to the list of supported
conversions when handling GL textures.
2021-10-06 02:16:43 -04:00
Matthias Clasen
b4bf26868a wip: 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.
2021-10-06 02:16:43 -04:00
Matthias Clasen
72ac0d0288 ngl: Refactor a bit
Break out a convert_texture routine that will
be reused in the future.
2021-10-06 02:16:43 -04:00
Matthias Clasen
5898e922a1 ngl: Fix caching for GL textures
Now that GL textures can have render data,
we need to check for that first.
2021-10-06 02:16:43 -04:00
Matthias Clasen
c425d318f0 ngl: Don't leak textures
When we linearize a GL texture, we need to keep
track of the result as a texture to be freed
eventually.
2021-10-06 02:16:43 -04:00
Matthias Clasen
fc929b8282 ngl: Refactor shaders a bit
We want to add more shaders using the same bits, so move
the functions to the preamble.
2021-10-06 02:16:43 -04:00
Matthias Clasen
b939184006 ngl: Update rendering for each frame
Look at whether the render nodes we are given
require HDR and if we can actually do HDR, and
update the surface if that is the case.
2021-10-06 02:16:43 -04:00
Matthias Clasen
73589ef038 Add gdk_surface_set_hdr
Add private api to tell a GdkSurface that it should
provide an HDR framebuffer (what that means is not
precisely specified).

Currently, this does nothing, since no backends
implement the new vfunc.
2021-10-06 02:16:43 -04:00
Matthias Clasen
90fb6c2729 ngl: Determine intermediate format
Look at the framebuffer and the rendernode to determine
what format to use for intermediate textures.

Our preference here is to use fp16, if we have it
and it makes sense for the framebuffer we're given.
2021-10-06 02:16:43 -04:00
Matthias Clasen
c79fd43cec ngl: Allow specifying texture formats
Allow passing a format when creating textures or render targets.

Update all callers to pass GL_RGBA8.
2021-10-06 02:16:43 -04:00
Matthias Clasen
b432d690ef gsk: Add hdr rendernode api
Add private api to find out if the content
of a render node should be considered HDR.

Currently, this comes down to whether there are
any texture nodes with HDR textures in the subtree.
2021-10-06 00:06:10 -04:00
Matthias Clasen
d800c1db6b gltexture: Add hdr api
Implement is_hdr for GdkGLTexture, by reusing
the machinery we have in place for downloading.
2021-10-06 00:06:10 -04:00
Matthias Clasen
3b93225fc7 texture: Add hdr api
Add private api to find out if a texture should be
considered as HDR. For now, we consider float formats
and >8bit formats to be HDR.
2021-10-06 00:06:10 -04:00
Matthias Clasen
e4e1b7c451 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.
2021-10-06 00:06:10 -04:00
Matthias Clasen
f80ef35dc0 ngl: 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.
2021-10-06 00:06:10 -04:00
Matthias Clasen
fe5e81757d Linearize gl textures
GL textures are typically in non-linear sRGB,
and in that case, we need to linearize them.
2021-10-06 00:06:10 -04:00
Matthias Clasen
a532b3dca5 ngl: 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.
2021-10-06 00:06:10 -04:00
Matthias Clasen
a6f8367c0a ngl: Apply gamma to frames
Use a shader to go from linear sRGB to sRGB when
we are done with a frame.
2021-10-06 00:06:10 -04:00
Matthias Clasen
da21a87de8 ngl: 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.
2021-10-06 00:06:10 -04:00
Matthias Clasen
97c11ffc23 ci: Update dependencies for msys
Add libpng, libjpeg-turbo, libtiff and lcms2.
2021-10-06 00:06:10 -04:00
Matthias Clasen
26dd0536f0 texture: Cosmetics
introspection complained
2021-10-06 00:04:53 -04:00
Matthias Clasen
33c739b0f9 memorytexture: Cosmetics
gi-docgen complained
2021-10-06 00:04:53 -04:00
Matthias Clasen
e4ba83ad4f colorprofile: Cosmetics
gi-docgen complained.
2021-10-05 23:00:53 -04:00
Benjamin Otte
17521e26f8 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.
2021-10-06 04:10:06 +02:00
Benjamin Otte
5a389874da FIXME: Add crude color management impl for cairo 2021-10-06 04:09:35 +02:00
Benjamin Otte
ce344b9267 x11: Implement support for color profiles
Stole the implementation from eog.

This doesn't yet update the profile when it changes though.
2021-10-06 04:09:35 +02:00
Benjamin Otte
66e6d6728f 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
ColorProfile has been attached to the target.
2021-10-06 04:09:35 +02:00
Matthias Clasen
4670c90312 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.
2021-10-06 04:09:35 +02:00
Matthias Clasen
b02cc7878a 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.
2021-10-06 04:09:35 +02:00
Matthias Clasen
5ada992ac9 Check profiles match memory formats
When creating a GdkMemoryTexture, the caller provides
a memory format and a color profile, both of which
encode expectations about the color components of
the pixel data. Check that they match.
2021-10-06 04:09:35 +02:00
Matthias Clasen
ba6c03e902 jpeg: Save color profile information
When writing a jpeg image, include the icc profile.
2021-10-06 04:09:35 +02:00
Matthias Clasen
9ae493c86e memorytexture: Cosmetics 2021-10-06 04:09:35 +02:00
Matthias Clasen
023ecbf028 tiff: Add color profile support
Apply an embedded icc profile when loading tiff
images, and save associated icc profile information
when saving tiff images.
2021-10-06 04:09:34 +02:00
Benjamin Otte
809c4d9ec1 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);
2021-10-06 04:09:34 +02:00
Benjamin Otte
a6fd0d0487 png: Refactor png saving
Do all the memory format shenanigans in GTK now and support all the PNG
formats.
2021-10-06 04:09:34 +02:00
Benjamin Otte
afb755a148 png: Handle color profiles 2021-10-06 04:09:34 +02:00
Benjamin Otte
3f2d0dbb27 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.
2021-10-06 04:09:34 +02:00
Benjamin Otte
b819b928ea 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.
2021-10-06 04:09:34 +02:00
Benjamin Otte
72b78d2da1 png: Add private formats for weird PNG stuff 2021-10-06 04:09:34 +02:00
Benjamin Otte
03ea3fd10b png: Do loader conversions in GDK
Not inside libpng.

We really want to do them in GL, but we don't have a premultiply step
yet.
2021-10-06 04:09:34 +02:00
Benjamin Otte
1b9243fed5 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
2021-10-06 04:09:34 +02:00
Benjamin Otte
422340a1e3 ngl: Avoid cairo in icon upload
Use the new memory texture magic
2021-10-06 04:09:34 +02:00
Benjamin Otte
60ebac2c71 gdk: Add srgb_linear color profile
... and gdk_color_profile_is_linear()
2021-10-06 04:09:34 +02:00
Benjamin Otte
3424f25a1f gdk: Make gdk_texture_download_surface() take a target colorprofile
This way, the resulting surface can contain the pixels in the desired
color profile.
2021-10-06 04:09:34 +02:00
Benjamin Otte
3fef469797 broadway: Use gdk_texture_save_to_png_bytes()
Instead of using Cairo, save the texture directly.
2021-10-06 04:09:34 +02:00
Benjamin Otte
1dd812a858 gdk: Rework gdk_pixbuf_get_from_texture() 2021-10-06 04:09:34 +02:00
Benjamin Otte
38730808a5 cairo: Add color profile get/set
Add a centralized place to attach color profiles to.

Nothing uses that information yet, but all the backends do set it.
2021-10-06 04:09:34 +02:00
Benjamin Otte
0dedbb5b17 gl: Move gdk_gl_context_upload_texture() into NGL
It's the only user and the API is kinda clunky, so it's easier to
refactor it there.

Also make use of gdk_memory_texture_convert().
2021-10-06 04:09:33 +02:00
Benjamin Otte
a433828f1a memorytexture: Add gdk_memory_texture_convert()
Converts a memory texture to a new one with a specific format and color
profile. You can even pass a rectangle to restrict the result to a given
region.

The code is well optimized, so it avoids copies if things match and will
in fact return the texture itself if the same colorspace and format and
no/the full rectangle are passed, so it can be used without fear of
performance implications.

In fact, the following code should be used - without any additional
checks - to turn any texture into a desired color profile and format
(say for saving with gdk-pixbuf):

memtex = gdk_texture_download (texture);
memtex = gdk_memory_texture_convert (memory_texture,
                                     GDK_MEMORY_R8G8B8A8,
                                     gdk_color_profile_get_srgb(),
                                     NULL);
pixbuf = gdk_pixbuf_new_from_data (gdk_memory_texture_get_data (memtex),
                                   GDK_COLOR_SPACE_RGB,
                                   TRUE,
                                   8
                                   gdk_texture_get_width (memtex),
                                   gdk_texture_get_height (memtex),
                                   gdk_memory_texture_get_stride (memtex),
                                   g_object_unref_wrapper,
                                   memtex);
2021-10-06 04:08:08 +02:00
Benjamin Otte
9624b67fdd gl: Move memory <=> GL format mapping
Put it into gdkmemoryformat.c, where all the mapping goes.
2021-10-06 04:08:08 +02:00
Benjamin Otte
0df63b0286 API: Add GdkSurface::color-profile
Unused so far, but there's a private setter for backends and a public
readable property for code.
2021-10-06 04:08:07 +02:00
Matthias Clasen
34f2fc9203 Revamp the color profile demo
Add an explicit 'no profile' image.
2021-10-06 04:08:07 +02:00
Matthias Clasen
f90c0a30df gtk-demo: Add a color profile demo
This is using test images from http://displaycal.net/.
2021-10-06 04:08:07 +02:00
Benjamin Otte
3b6ce78400 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.
2021-10-06 04:08:07 +02:00
Benjamin Otte
e7ba7d5540 jpeg: Parse ICC profiles
.. and pass them on.
2021-10-06 04:08:07 +02:00
Benjamin Otte
0ac0a5914c 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.
2021-10-06 04:08:07 +02:00
Benjamin Otte
d976bf910c gl: Refactor texture upload code
Do the texture unpacking at the target, not at the source.

Also handle color profiles.
2021-10-06 04:08:07 +02:00
Benjamin Otte
db2aab289c memoryformat: Take a color profile when converting 2021-10-06 04:08:07 +02:00
Benjamin Otte
85e4e47d30 memorytexture: Split out GdkMemoryFormat handling
Also, now make gdk_memory_convert() the only conversion functions
and allow conversions between any 2 formats by going via a float[4].

This could be optimized via fast-paths, but so far it isn't.
2021-10-06 04:08:07 +02:00
Benjamin Otte
32740934b4 texture: Add a ::color-profile property
Returns the associated color profile. For now, this is always sRGB.
2021-10-06 04:08:07 +02:00
Benjamin Otte
8acf1b78cf gdk: Add GdkColorProfile
The code doesn't do anything yet, this is just the boilerplate.
2021-10-06 04:08:07 +02:00
Benjamin Otte
957a6505ca cms: Add lcms to the build 2021-10-06 04:08:07 +02:00
100 changed files with 5272 additions and 1253 deletions

View File

@@ -31,7 +31,11 @@ pacman --noconfirm -S --needed \
mingw-w64-$MSYS2_ARCH-pango \
mingw-w64-$MSYS2_ARCH-fribidi \
mingw-w64-$MSYS2_ARCH-gst-plugins-bad \
mingw-w64-$MSYS2_ARCH-shared-mime-info
mingw-w64-$MSYS2_ARCH-shared-mime-info \
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

@@ -18,6 +18,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>
@@ -254,6 +265,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.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

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

@@ -118,5 +118,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

@@ -1342,13 +1342,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>
@@ -1357,13 +1517,91 @@ 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>

View File

@@ -609,56 +609,39 @@ open_shared_memory (void)
return ret;
}
typedef struct {
int fd;
gsize size;
} PngData;
static cairo_status_t
write_png_cb (void *closure,
const guchar *data,
unsigned int length)
{
PngData *png_data = closure;
int fd = png_data->fd;
while (length)
{
gssize ret = write (fd, data, length);
if (ret <= 0)
return CAIRO_STATUS_WRITE_ERROR;
png_data->size += ret;
length -= ret;
data += ret;
}
return CAIRO_STATUS_SUCCESS;
}
guint32
gdk_broadway_server_upload_texture (GdkBroadwayServer *server,
GdkTexture *texture)
{
guint32 id;
cairo_surface_t *surface = gdk_texture_download_surface (texture);
BroadwayRequestUploadTexture msg;
PngData data;
GBytes *bytes;
const guchar *data;
gsize size;
int fd;
id = server->next_texture_id++;
data.fd = open_shared_memory ();
data.size = 0;
cairo_surface_write_to_png_stream (surface, write_png_cb, &data);
bytes = gdk_texture_save_to_png_bytes (texture);
fd = open_shared_memory ();
data = g_bytes_get_data (bytes, &size);
msg.id = id;
msg.offset = 0;
msg.size = data.size;
while (size)
{
gssize ret = write (fd, data, size);
if (ret <= 0)
break;
size -= ret;
data += ret;
}
g_bytes_unref (bytes);
/* This passes ownership of fd */
gdk_broadway_server_send_fd_message (server, msg,
BROADWAY_REQUEST_UPLOAD_TEXTURE, data.fd);
BROADWAY_REQUEST_UPLOAD_TEXTURE, fd);
return id;
}

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

@@ -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 },
{ "hdr", GDK_DEBUG_HDR, "Use HDR 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>
@@ -38,6 +39,7 @@
#include <gdk/gdkcontentproviderimpl.h>
#include <gdk/gdkcontentserializer.h>
#include <gdk/gdkcursor.h>
#include <gdk/gdkderivedprofile.h>
#include <gdk/gdkdevice.h>
#include <gdk/gdkdevicepad.h>
#include <gdk/gdkdevicetool.h>
@@ -53,6 +55,7 @@
#include <gdk/gdkframetimings.h>
#include <gdk/gdkglcontext.h>
#include <gdk/gdkgltexture.h>
#include <gdk/gdkiccprofile.h>
#include <gdk/gdkkeys.h>
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkmemorytexture.h>

View File

@@ -19,6 +19,10 @@
#include "gdkcairoprivate.h"
#include "gdkcolorprivate.h"
#include "gdkcolorprofile.h"
#include "gdkmemoryformatprivate.h"
#include <math.h>
/**
@@ -32,14 +36,20 @@ void
gdk_cairo_set_source_rgba (cairo_t *cr,
const GdkRGBA *rgba)
{
GdkColor color;
const float *components;
g_return_if_fail (cr != NULL);
g_return_if_fail (rgba != NULL);
gdk_color_convert_rgba (&color, gdk_cairo_get_color_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);
}
/**
@@ -399,3 +409,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.6
**/
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.6
**/
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.6
**/
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

@@ -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_6
void gdk_cairo_surface_set_color_profile (cairo_surface_t *surface,
GdkColorProfile *profile);
GDK_AVAILABLE_IN_4_6
GdkColorProfile * gdk_cairo_surface_get_color_profile (cairo_surface_t *surface);
GDK_AVAILABLE_IN_4_6
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,

160
gdk/gdkcolor.c Normal file
View File

@@ -0,0 +1,160 @@
/* 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 "gdkiccprofileprivate.h"
#include "gdkderivedprofile.h"
static inline cmsHTRANSFORM *
gdk_color_get_transform (GdkICCProfile *src,
GdkICCProfile *dest)
{
cmsHPROFILE *src_profile, *dest_profile;
src_profile = gdk_icc_profile_get_lcms_profile (GDK_ICC_PROFILE (src));
dest_profile = gdk_icc_profile_get_lcms_profile (GDK_ICC_PROFILE (dest));
return gdk_icc_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)
{
GdkColorProfile *src_profile, *dest_profile;
gsize n_components;
float *in;
n_components = gdk_color_profile_get_n_components (profile);
gdk_color_init (self, profile, other->alpha, NULL, n_components);
if (GDK_IS_DERIVED_PROFILE (other->profile))
{
in = g_alloca (sizeof (float) * n_components);
gdk_derived_profile_convert_to_base_profile (GDK_DERIVED_PROFILE (other->profile),
gdk_color_get_components (other),
in);
src_profile = gdk_derived_profile_get_base_profile (GDK_DERIVED_PROFILE (other->profile));
}
else
{
in = (float *)gdk_color_get_components (other);
src_profile = other->profile;
}
if (GDK_IS_DERIVED_PROFILE (profile))
dest_profile = gdk_derived_profile_get_base_profile (GDK_DERIVED_PROFILE (profile));
else
dest_profile = profile;
cmsDoTransform (gdk_color_get_transform (GDK_ICC_PROFILE (src_profile),
GDK_ICC_PROFILE (dest_profile)),
in,
(float *) gdk_color_get_components (self),
1);
if (GDK_IS_DERIVED_PROFILE (profile))
gdk_derived_profile_convert_from_base_profile (GDK_DERIVED_PROFILE (profile),
gdk_color_get_components (self),
(float *)gdk_color_get_components (self));
}
void
gdk_color_convert_rgba (GdkColor *self,
GdkColorProfile *profile,
const GdkRGBA *rgba)
{
GdkColorProfile *dest_profile;
gsize n_components;
n_components = gdk_color_profile_get_n_components (profile);
gdk_color_init (self, profile, rgba->alpha, NULL, n_components);
if (GDK_IS_DERIVED_PROFILE (profile))
dest_profile = gdk_derived_profile_get_base_profile (GDK_DERIVED_PROFILE (profile));
else
dest_profile = profile;
cmsDoTransform (gdk_color_get_transform (GDK_ICC_PROFILE (gdk_color_profile_get_srgb ()),
GDK_ICC_PROFILE (dest_profile)),
(float[3]) { rgba->red, rgba->green, rgba->blue },
(float *) gdk_color_get_components (self),
1);
if (GDK_IS_DERIVED_PROFILE (profile))
gdk_derived_profile_convert_from_base_profile (GDK_DERIVED_PROFILE (profile),
gdk_color_get_components (self),
(float *)gdk_color_get_components (self));
}
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));
}

146
gdk/gdkcolorprofile.c Normal file
View File

@@ -0,0 +1,146 @@
/* 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 using [property@Gdk.ColorProfile:icc-profile]
* property.
*
* `GdkColorProfile` objects are immutable and therefore threadsafe.
*
* Since: 4.6
*/
#include "config.h"
#include "gdkcolorprofileprivate.h"
#include "gdkintl.h"
G_DEFINE_TYPE (GdkColorProfile, gdk_color_profile, G_TYPE_OBJECT)
static void
gdk_color_profile_init (GdkColorProfile *profile)
{
}
static gboolean
gdk_color_profile_real_is_linear (GdkColorProfile *profile)
{
return FALSE;
}
static gsize
gdk_color_profile_real_get_n_components (GdkColorProfile *profile)
{
return 0;
}
static gboolean
gdk_color_profile_real_equal (gconstpointer profile1,
gconstpointer profile2)
{
return profile1 == profile2;
}
static void
gdk_color_profile_class_init (GdkColorProfileClass *klass)
{
klass->is_linear = gdk_color_profile_real_is_linear;
klass->get_n_components = gdk_color_profile_real_get_n_components;
klass->equal = gdk_color_profile_real_equal;
}
/**
* 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.6
*/
gboolean
gdk_color_profile_is_linear (GdkColorProfile *self)
{
g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), FALSE);
return GDK_COLOR_PROFILE_GET_CLASS (self)->is_linear (self);
}
/**
* 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.
*
* Returns: The number of components
**/
gsize
gdk_color_profile_get_n_components (GdkColorProfile *self)
{
g_return_val_if_fail (GDK_IS_COLOR_PROFILE (self), 3);
return GDK_COLOR_PROFILE_GET_CLASS (self)->get_n_components (self);
}
/**
* 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.6
*/
gboolean
gdk_color_profile_equal (gconstpointer profile1,
gconstpointer profile2)
{
if (profile1 == profile2)
return TRUE;
if (G_OBJECT_TYPE (profile1) != G_OBJECT_TYPE (profile2))
return FALSE;
return GDK_COLOR_PROFILE_GET_CLASS (profile1)->equal (profile1, profile2);
}

69
gdk/gdkcolorprofile.h Normal file
View File

@@ -0,0 +1,69 @@
/* 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_6
GType gdk_color_profile_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_4_6
GdkColorProfile * gdk_color_profile_get_srgb (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_4_6
GdkColorProfile * gdk_color_profile_new_from_icc_bytes (GBytes *bytes,
GError **error);
GDK_AVAILABLE_IN_4_6
GBytes * gdk_color_profile_get_icc_profile (GdkColorProfile *self);
GDK_AVAILABLE_IN_4_6
gboolean gdk_color_profile_is_linear (GdkColorProfile *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_6
gsize gdk_color_profile_get_n_components (GdkColorProfile *self) G_GNUC_PURE;
GDK_AVAILABLE_IN_4_6
gboolean gdk_color_profile_equal (gconstpointer profile1,
gconstpointer profile2);
G_END_DECLS
#endif /* __GDK_COLOR_PROFILE_H__ */

View File

@@ -0,0 +1,32 @@
#ifndef __GDK_COLOR_PROFILE_PRIVATE_H__
#define __GDK_COLOR_PROFILE_PRIVATE_H__
#include "gdkcolorprofile.h"
#include "gdkmemorytexture.h"
#include <lcms2.h>
G_BEGIN_DECLS
struct _GdkColorProfile
{
GObject parent_instance;
};
struct _GdkColorProfileClass
{
GObjectClass parent_class;
gboolean (* is_linear) (GdkColorProfile *profile);
gsize (* get_n_components) (GdkColorProfile *profile);
gboolean (* equal) (gconstpointer profile1,
gconstpointer profile2);
};
GdkColorProfile * gdk_color_profile_get_srgb_linear (void) G_GNUC_CONST;
GdkColorProfile * gdk_color_profile_get_hsl (void) G_GNUC_CONST;
GdkColorProfile * gdk_color_profile_get_hwb (void) G_GNUC_CONST;
G_END_DECLS
#endif /* __GDK_COLOR_PROFILE_PRIVATE_H__ */

View File

@@ -641,11 +641,7 @@ pixbuf_serializer (GdkContentSerializer *serializer)
else if (G_VALUE_HOLDS (value, GDK_TYPE_TEXTURE))
{
GdkTexture *texture = g_value_get_object (value);
cairo_surface_t *surface = gdk_texture_download_surface (texture);
pixbuf = gdk_pixbuf_get_from_surface (surface,
0, 0,
gdk_texture_get_width (texture), gdk_texture_get_height (texture));
cairo_surface_destroy (surface);
pixbuf = gdk_pixbuf_get_from_texture (texture);
}
else
{

View File

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

513
gdk/gdkderivedprofile.c Normal file
View File

@@ -0,0 +1,513 @@
/* gdkderivedprofile.c
*
* Copyright 2021 (c) Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* GdkDerivedProfile:
*
* Since: 4.6
*/
#include "config.h"
#include "gdkderivedprofile.h"
#include "gdkcolorprofileprivate.h"
#include "gdkenumtypes.h"
#include "gdkintl.h"
struct _GdkDerivedProfile
{
GdkColorProfile parent_instance;
GdkColorSpace color_space;
GdkColorProfile *base_profile;
};
struct _GdkDerivedProfileClass
{
GdkColorProfileClass parent_class;
};
enum {
PROP_0,
PROP_BASE_PROFILE,
PROP_COLOR_SPACE,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
G_DEFINE_TYPE (GdkDerivedProfile, gdk_derived_profile, GDK_TYPE_COLOR_PROFILE)
static void
gdk_derived_profile_init (GdkDerivedProfile *profile)
{
}
static void
gdk_derived_profile_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GdkDerivedProfile *self = GDK_DERIVED_PROFILE (object);
switch (prop_id)
{
case PROP_BASE_PROFILE:
self->base_profile = g_value_dup_object (value);
break;
case PROP_COLOR_SPACE:
self->color_space = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gdk_derived_profile_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GdkDerivedProfile *self = GDK_DERIVED_PROFILE (object);
switch (prop_id)
{
case PROP_BASE_PROFILE:
g_value_set_object (value, self->base_profile);
break;
case PROP_COLOR_SPACE:
g_value_set_enum (value, self->color_space);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gdk_derived_profile_dispose (GObject *object)
{
GdkDerivedProfile *self = GDK_DERIVED_PROFILE (object);
g_clear_object (&self->base_profile);
G_OBJECT_CLASS (gdk_derived_profile_parent_class)->dispose (object);
}
static gboolean
gdk_derived_profile_is_linear (GdkColorProfile *profile)
{
GdkDerivedProfile *self = GDK_DERIVED_PROFILE (profile);
return self->base_profile == gdk_color_profile_get_srgb_linear ();
}
static gsize
gdk_derived_profile_get_n_components (GdkColorProfile *profile)
{
GdkDerivedProfile *self = GDK_DERIVED_PROFILE (profile);
return gdk_color_profile_get_n_components (self->base_profile);
}
static gboolean
gdk_derived_profile_equal (gconstpointer profile1,
gconstpointer profile2)
{
GdkDerivedProfile *p1 = GDK_DERIVED_PROFILE (profile1);
GdkDerivedProfile *p2 = GDK_DERIVED_PROFILE (profile2);
return p1->color_space == p2->color_space &&
gdk_color_profile_equal (p1->base_profile, p2->base_profile);
}
static void
gdk_derived_profile_class_init (GdkDerivedProfileClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GdkColorProfileClass *profile_class = GDK_COLOR_PROFILE_CLASS (klass);
gobject_class->set_property = gdk_derived_profile_set_property;
gobject_class->get_property = gdk_derived_profile_get_property;
gobject_class->dispose = gdk_derived_profile_dispose;
profile_class->is_linear = gdk_derived_profile_is_linear;
profile_class->get_n_components = gdk_derived_profile_get_n_components;
profile_class->equal = gdk_derived_profile_equal;
/**
* GdkDerivedProfile:base-profile: (attributes org.gtk.Property.get=gdk_derived_profile_get_base_profile)
*
* The base profile for this color profile.
*/
properties[PROP_BASE_PROFILE] =
g_param_spec_object ("base-profile",
P_("Base profile"),
P_("Base profile for this color profile"),
GDK_TYPE_ICC_PROFILE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS |
G_PARAM_EXPLICIT_NOTIFY);
/**
* GdkDerivedProfile:color-space: (attributes org.gtk.Property.get=gdk_derived_profile_get_color_space)
*
* The color space for this color profile.
*/
properties[PROP_COLOR_SPACE] =
g_param_spec_enum ("color-space",
P_("Color space"),
P_("Color space for this color profile"),
GDK_TYPE_COLOR_SPACE,
GDK_COLOR_SPACE_HSL,
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);
}
/**
* gdk_derived_profile_get_base_profile:
* @profile: a `GdkDerivedProfile`
*
* Returns the base profile for this profile.
*
* Returns: (transfer none): the base profile
*
* Since: 4.6
*/
GdkColorProfile *
gdk_derived_profile_get_base_profile (GdkDerivedProfile *self)
{
return self->base_profile;
}
/**
* gdk_derived_profile_get_color_space:
* @profile: a `GdkDerivedProfile`
*
* Returns the color space for this profile.
*
* Returns: the color space
*
* Since: 4.6
*/
GdkColorSpace
gdk_derived_profile_get_color_space (GdkDerivedProfile *self)
{
return self->color_space;
}
/*<private>
* gdk_color_profile_get_hsl:
*
* Returns the color profile corresponding to the HSL
* color space.
*
* It can display the same colors as sRGB, but it encodes
* the coordinates differently.
*
* Returns: (transfer none): the color profile for the HSL
* color space.
*/
GdkColorProfile *
gdk_color_profile_get_hsl (void)
{
static GdkColorProfile *hsl_profile;
if (g_once_init_enter (&hsl_profile))
{
GdkColorProfile *new_profile;
new_profile = g_object_new (GDK_TYPE_DERIVED_PROFILE,
"base-profile", gdk_color_profile_get_srgb (),
"color-space", GDK_COLOR_SPACE_HSL,
NULL);
g_assert (new_profile);
g_once_init_leave (&hsl_profile, new_profile);
}
return hsl_profile;
}
GdkColorProfile *
gdk_color_profile_get_hwb (void)
{
static GdkColorProfile *hwb_profile;
if (g_once_init_enter (&hwb_profile))
{
GdkColorProfile *new_profile;
new_profile = g_object_new (GDK_TYPE_DERIVED_PROFILE,
"base-profile", gdk_color_profile_get_srgb (),
"color-space", GDK_COLOR_SPACE_HWB,
NULL);
g_assert (new_profile);
g_once_init_leave (&hwb_profile, new_profile);
}
return hwb_profile;
}
static void
hsl_to_rgb (const float *in,
float *out)
{
float lightness;
float saturation;
float m1, m2;
float orig_hue, hue;
lightness = in[2];
saturation = in[1];
orig_hue = in[0];
if (lightness <= 0.5)
m2 = lightness * (1 + saturation);
else
m2 = lightness + saturation - lightness * saturation;
m1 = 2 * lightness - m2;
if (saturation == 0)
{
out[0] = out[1] = out[2] = lightness;
}
else
{
hue = orig_hue + 120;
while (hue > 360)
hue -= 360;
while (hue < 0)
hue += 360;
if (hue < 60)
out[0] = m1 + (m2 - m1) * hue / 60;
else if (hue < 180)
out[0] = m2;
else if (hue < 240)
out[0] = m1 + (m2 - m1) * (240 - hue) / 60;
else
out[0] = m1;
hue = orig_hue;
while (hue > 360)
hue -= 360;
while (hue < 0)
hue += 360;
if (hue < 60)
out[1] = m1 + (m2 - m1) * hue / 60;
else if (hue < 180)
out[1] = m2;
else if (hue < 240)
out[1] = m1 + (m2 - m1) * (240 - hue) / 60;
else
out[1] = m1;
hue = orig_hue - 120;
while (hue > 360)
hue -= 360;
while (hue < 0)
hue += 360;
if (hue < 60)
out[2] = m1 + (m2 - m1) * hue / 60;
else if (hue < 180)
out[2] = m2;
else if (hue < 240)
out[2] = m1 + (m2 - m1) * (240 - hue) / 60;
else
out[2] = m1;
}
}
static void
rgb_to_hsl (const float *in,
float *out)
{
float min;
float max;
float red;
float green;
float blue;
float delta;
float hue;
float saturation;
float lightness;
red = in[0];
green = in[1];
blue = in[2];
if (red > green)
{
if (red > blue)
max = red;
else
max = blue;
if (green < blue)
min = green;
else
min = blue;
}
else
{
if (green > blue)
max = green;
else
max = blue;
if (red < blue)
min = red;
else
min = blue;
}
lightness = (max + min) / 2;
saturation = 0;
hue = 0;
if (max != min)
{
if (lightness <= 0.5)
saturation = (max - min) / (max + min);
else
saturation = (max - min) / (2 - max - min);
delta = max -min;
if (red == max)
hue = (green - blue) / delta;
else if (green == max)
hue = 2 + (blue - red) / delta;
else if (blue == max)
hue = 4 + (red - green) / delta;
hue *= 60;
if (hue < 0.0)
hue += 360;
}
out[0] = hue;
out[1] = saturation;
out[2] = lightness;
}
static void
hwb_to_rgb (const float *in,
float *out)
{
float hue;
float white;
float black;
float m[3];
hue = in[0];
white = in[1];
black = in[2];
white /= 100;
black /= 100;
if (white + black >= 1)
{
out[0] = out[1] = out[2] = white / (white + black);
return;
}
m[0] = hue;
m[1] = 1;
m[2] = 0.5;
hsl_to_rgb (m, out);
out[0] = out[0] * (1 - white - black) + white;
out[1] = out[1] * (1 - white - black) + white;
out[2] = out[2] * (1 - white - black) + white;
}
static void
rgb_to_hwb (const float *in,
float *out)
{
float white;
float black;
rgb_to_hsl (in, out);
white = MIN (MIN (out[0], out[1]), out[2]);
black = 1 - MAX (MAX (out[0], out[1]), out[2]);
out[1] = white * 100;
out[2] = black * 100;
}
void
gdk_derived_profile_convert_to_base_profile (GdkDerivedProfile *profile,
const float *in,
float *out)
{
switch (gdk_derived_profile_get_color_space (profile))
{
case GDK_COLOR_SPACE_HSL:
hsl_to_rgb (in, out);
break;
case GDK_COLOR_SPACE_HWB:
hwb_to_rgb (in, out);
break;
default:
g_assert_not_reached ();
}
}
void
gdk_derived_profile_convert_from_base_profile (GdkDerivedProfile *profile,
const float *in,
float *out)
{
switch (gdk_derived_profile_get_color_space (profile))
{
case GDK_COLOR_SPACE_HSL:
rgb_to_hsl (in, out);
break;
case GDK_COLOR_SPACE_HWB:
rgb_to_hwb (in, out);
break;
default:
g_assert_not_reached ();
}
}

71
gdk/gdkderivedprofile.h Normal file
View File

@@ -0,0 +1,71 @@
/* gdkderivedprofile.h
*
* Copyright 2021 (c) Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GDK_DERIVED_PROFILE_H__
#define __GDK_DERIVED_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/gdkcolorprofile.h>
#include <gdk/gdkiccprofile.h>
G_BEGIN_DECLS
#define GDK_TYPE_DERIVED_PROFILE (gdk_derived_profile_get_type ())
#define GDK_DERIVED_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_DERIVED_PROFILE, GdkDerivedProfile))
#define GDK_IS_DERIVED_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_DERIVED_PROFILE))
#define GDK_DERIVED_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_DERIVED_PROFILE, GdkDerivedProfileClass))
#define GDK_IS_DERIVED_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_DERIVED_PROFILE))
#define GDK_DERIVED_PROFILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_DERIVED_PROFILE, GdkDerivedProfileClass))
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkDerivedProfile, g_object_unref)
typedef struct _GdkDerivedProfileClass GdkDerivedProfileClass;
GDK_AVAILABLE_IN_4_6
GType gdk_derived_profile_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_4_6
GdkColorProfile * gdk_derived_profile_get_base_profile (GdkDerivedProfile *profile);
typedef enum {
GDK_COLOR_SPACE_HSL,
GDK_COLOR_SPACE_HWB,
} GdkColorSpace;
GDK_AVAILABLE_IN_4_6
GdkColorSpace gdk_derived_profile_get_color_space (GdkDerivedProfile *profile);
GDK_AVAILABLE_IN_4_6
void gdk_derived_profile_convert_to_base_profile (GdkDerivedProfile *profile,
const float *in,
float *out);
GDK_AVAILABLE_IN_4_6
void gdk_derived_profile_convert_from_base_profile (GdkDerivedProfile *profile,
const float *in,
float *out);
G_END_DECLS
#endif /* __GDK_DERIVED_PROFILE_H__ */

View File

@@ -76,9 +76,11 @@
#include "gdkglcontextprivate.h"
#include "gdkcolorprofile.h"
#include "gdkdebug.h"
#include "gdkdisplayprivate.h"
#include "gdkintl.h"
#include "gdkmemoryformatprivate.h"
#include "gdkmemorytextureprivate.h"
#include "gdkprofilerprivate.h"
@@ -227,125 +229,6 @@ gdk_gl_context_get_property (GObject *gobject,
}
}
void
gdk_gl_context_upload_texture (GdkGLContext *context,
const guchar *data,
int width,
int height,
int stride,
GdkMemoryFormat data_format,
guint texture_target)
{
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
guchar *copy = NULL;
GLint gl_internalformat;
GLint gl_format;
GLint gl_type;
gsize bpp;
g_return_if_fail (GDK_IS_GL_CONTEXT (context));
if (!priv->use_es && data_format == GDK_MEMORY_DEFAULT) /* Cairo surface format */
{
gl_internalformat = GL_RGBA8;
gl_format = GL_BGRA;
gl_type = GL_UNSIGNED_INT_8_8_8_8_REV;
}
else if (data_format == GDK_MEMORY_R8G8B8) /* Pixmap non-alpha data */
{
gl_internalformat = GL_RGBA8;
gl_format = GL_RGB;
gl_type = GL_UNSIGNED_BYTE;
}
else if (priv->use_es && data_format == GDK_MEMORY_B8G8R8)
{
gl_internalformat = GL_RGBA8;
gl_format = GL_BGR;
gl_type = GL_UNSIGNED_BYTE;
}
else if (data_format == GDK_MEMORY_R16G16B16)
{
gl_internalformat = GL_RGBA16;
gl_format = GL_RGB;
gl_type = GL_UNSIGNED_SHORT;
}
else if (data_format == GDK_MEMORY_R16G16B16A16_PREMULTIPLIED)
{
gl_internalformat = GL_RGBA16;
gl_format = GL_RGBA;
gl_type = GL_UNSIGNED_SHORT;
}
else if (data_format == GDK_MEMORY_R16G16B16_FLOAT)
{
gl_internalformat = GL_RGB16F;
gl_format = GL_RGB;
gl_type = GL_HALF_FLOAT;
}
else if (data_format == GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED)
{
gl_internalformat = GL_RGBA16F;
gl_format = GL_RGBA;
gl_type = GL_HALF_FLOAT;
}
else if (data_format == GDK_MEMORY_R32G32B32_FLOAT)
{
gl_internalformat = GL_RGB32F;
gl_format = GL_RGB;
gl_type = GL_FLOAT;
}
else if (data_format == GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED)
{
gl_internalformat = GL_RGBA32F;
gl_format = GL_RGBA;
gl_type = GL_FLOAT;
}
else /* Fall-back, convert to GLES format */
{
copy = g_malloc (width * height * 4);
gdk_memory_convert (copy, width * 4,
GDK_MEMORY_CONVERT_GLES_RGBA,
data, stride, data_format,
width, height);
data_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
stride = width * 4;
data = copy;
gl_internalformat = GL_RGBA8;
gl_format = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;
}
bpp = gdk_memory_format_bytes_per_pixel (data_format);
/* 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 == width * bpp)
{
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
}
else if ((!priv->use_es ||
(priv->use_es && (priv->gl_version >= 30 || priv->has_unpack_subimage))))
{
glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp);
glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
}
else
{
int i;
glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, NULL);
for (i = 0; i < height; i++)
glTexSubImage2D (texture_target, 0, 0, i, width, 1, gl_format, gl_type, data + (i * stride));
}
g_free (copy);
}
#define N_EGL_ATTRS 16
static gboolean
@@ -1558,6 +1441,19 @@ gdk_gl_context_get_version (GdkGLContext *context,
*minor = priv->gl_version % 10;
}
gboolean
gdk_gl_context_has_version (GdkGLContext *context,
int major,
int minor)
{
GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context);
g_return_val_if_fail (GDK_IS_GL_CONTEXT (context), FALSE);
g_return_val_if_fail (minor < 10, FALSE);
return priv->gl_version >= major * 10 + minor;
}
/**
* gdk_gl_context_clear_current:
*

View File

@@ -105,13 +105,6 @@ GdkGLContext * gdk_gl_context_new_for_surface (GdkSurface
void gdk_gl_context_set_is_legacy (GdkGLContext *context,
gboolean is_legacy);
void gdk_gl_context_upload_texture (GdkGLContext *context,
const guchar *data,
int width,
int height,
int stride,
GdkMemoryFormat data_format,
guint texture_target);
gboolean gdk_gl_context_has_unpack_subimage (GdkGLContext *context);
void gdk_gl_context_push_debug_group (GdkGLContext *context,
const char *message);
@@ -129,6 +122,9 @@ void gdk_gl_context_label_object_printf (GdkGLContext
const char *format,
...) G_GNUC_PRINTF (4, 5);
gboolean gdk_gl_context_has_version (GdkGLContext *context,
int major,
int minor);
gboolean gdk_gl_context_has_debug (GdkGLContext *self) G_GNUC_PURE;
gboolean gdk_gl_context_use_es_bgra (GdkGLContext *context);

View File

@@ -21,8 +21,10 @@
#include "gdkgltextureprivate.h"
#include "gdkdisplayprivate.h"
#include "gdkmemoryformatprivate.h"
#include "gdkmemorytextureprivate.h"
#include "gdktextureprivate.h"
#include "gdkcolorprofile.h"
#include <epoxy/gl.h>
@@ -36,6 +38,7 @@ struct _GdkGLTexture {
GdkTexture parent_instance;
GdkGLContext *context;
GdkGLTextureFlags flags;
guint id;
GdkTexture *saved;
@@ -139,17 +142,20 @@ gdk_gl_texture_get_tex_image (GdkGLTexture *self,
data);
}
}
static void
gdk_gl_texture_do_download_texture (gpointer texture_,
gpointer result_)
{
GdkTexture *texture = texture_;
GdkTexture **result = result_;
typedef struct {
GdkMemoryFormat format;
GLint gl_format;
GLint gl_type;
} GdkGLTextureFormat;
static void
gdk_gl_texture_get_format (gpointer texture_,
gpointer result_)
{
GdkGLTextureFormat *result = result_;
GLint internal_format, gl_format, gl_type;
guchar *data;
gsize stride;
GBytes *bytes;
GdkMemoryFormat format;
glGetTexLevelParameteriv (GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internal_format);
@@ -213,18 +219,36 @@ gdk_gl_texture_do_download_texture (gpointer texture_,
break;
}
stride = gdk_memory_format_bytes_per_pixel (format) * texture->width;
result->format = format;
result->gl_format = gl_format;
result->gl_type = gl_type;
}
static void
gdk_gl_texture_do_download_texture (gpointer texture_,
gpointer result_)
{
GdkTexture *texture = texture_;
GdkTexture **result = result_;
GdkGLTextureFormat format;
gsize stride;
guchar *data;
GBytes *bytes;
gdk_gl_texture_get_format (texture, &format);
stride = gdk_memory_format_bytes_per_pixel (format.format) * texture->width;
data = g_malloc (stride * texture->height);
gdk_gl_texture_get_tex_image (texture_,
gl_format,
gl_type,
format.gl_format,
format.gl_type,
data);
bytes = g_bytes_new_take (data, stride * texture->height);
*result = gdk_memory_texture_new (texture->width,
texture->height,
format,
format.format,
bytes,
stride);
@@ -319,6 +343,17 @@ gdk_gl_texture_download_float (GdkTexture *texture,
gdk_gl_texture_run (self, gdk_gl_texture_do_download_float, data);
}
static gboolean
gdk_gl_texture_is_hdr (GdkTexture *texture)
{
GdkGLTexture *self = GDK_GL_TEXTURE (texture);
GdkGLTextureFormat result;
gdk_gl_texture_run (self, gdk_gl_texture_get_format, &result);
return gdk_memory_format_is_hdr (result.format);
}
static void
gdk_gl_texture_class_init (GdkGLTextureClass *klass)
{
@@ -328,6 +363,7 @@ gdk_gl_texture_class_init (GdkGLTextureClass *klass)
texture_class->download_texture = gdk_gl_texture_download_texture;
texture_class->download = gdk_gl_texture_download;
texture_class->download_float = gdk_gl_texture_download_float;
texture_class->is_hdr = gdk_gl_texture_is_hdr;
gobject_class->dispose = gdk_gl_texture_dispose;
}
@@ -348,6 +384,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
@@ -402,6 +444,45 @@ 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.
*
* 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.6
*/
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;
@@ -413,10 +494,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,28 @@ GdkTexture * gdk_gl_texture_new (GdkGLContext
GDestroyNotify destroy,
gpointer data);
/**
* GdkGLTextureFlags:
* @GDK_GL_TEXTURE_FLAGS_PREMULTIPLIED: The alpha in the data is not premultiplied
* @GDK_GL_TEXTURE_FLAGS_FLIPPED: The data has the origin at the bottom
*
* 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_6
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

451
gdk/gdkiccprofile.c Normal file
View File

@@ -0,0 +1,451 @@
/* gdkiccprofile.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/>.
*/
/**
* GdkICCProfile:
*
* `GdkICCProfile` is used to describe ICC color profiles.
*
* 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 using [property@Gdk.ColorProfile:icc-profile]
* property.
*
* `GdkICCProfile` objects are immutable and therefore threadsafe.
*
* Since: 4.6
*/
#include "config.h"
#include "gdkiccprofileprivate.h"
#include "gdkcolorprofileprivate.h"
#include "gdkintl.h"
struct _GdkICCProfile
{
GdkColorProfile parent_instance;
GBytes *icc_profile;
cmsHPROFILE lcms_profile;
};
struct _GdkICCProfileClass
{
GdkColorProfileClass parent_class;
};
enum {
PROP_0,
PROP_ICC_PROFILE,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
static gboolean
gdk_icc_profile_real_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
{
GdkICCProfile *self = GDK_ICC_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;
}
}
return TRUE;
}
static void
gdk_icc_profile_initable_init (GInitableIface *iface)
{
iface->init = gdk_icc_profile_real_init;
}
G_DEFINE_TYPE_WITH_CODE (GdkICCProfile, gdk_icc_profile, GDK_TYPE_COLOR_PROFILE,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gdk_icc_profile_initable_init))
static void
gdk_icc_profile_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GdkICCProfile *self = GDK_ICC_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_icc_profile_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GdkICCProfile *self = GDK_ICC_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_icc_profile_dispose (GObject *object)
{
GdkICCProfile *self = GDK_ICC_PROFILE (object);
g_clear_pointer (&self->icc_profile, g_bytes_unref);
g_clear_pointer (&self->lcms_profile, cmsCloseProfile);
G_OBJECT_CLASS (gdk_icc_profile_parent_class)->dispose (object);
}
static gboolean
gdk_icc_profile_is_linear (GdkColorProfile *profile)
{
return profile == gdk_color_profile_get_srgb_linear ();
}
static gsize
gdk_icc_profile_get_n_components (GdkColorProfile *profile)
{
GdkICCProfile *self = GDK_ICC_PROFILE (profile);
return cmsChannelsOf (cmsGetColorSpace (self->lcms_profile));
}
static gboolean
gdk_icc_profile_equal (gconstpointer profile1,
gconstpointer profile2)
{
return g_bytes_equal (GDK_ICC_PROFILE (profile1)->icc_profile,
GDK_ICC_PROFILE (profile2)->icc_profile);
}
static void
gdk_icc_profile_class_init (GdkICCProfileClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GdkColorProfileClass *profile_class = GDK_COLOR_PROFILE_CLASS (klass);
gobject_class->set_property = gdk_icc_profile_set_property;
gobject_class->get_property = gdk_icc_profile_get_property;
gobject_class->dispose = gdk_icc_profile_dispose;
profile_class->is_linear = gdk_icc_profile_is_linear;
profile_class->get_n_components = gdk_icc_profile_get_n_components;
profile_class->equal = gdk_icc_profile_equal;
/**
* GdkICCProfile:icc-profile: (attributes org.gtk.Property.get=gdk_icc_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_icc_profile_init (GdkICCProfile *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.6
*/
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 (gdk_icc_profile_new_from_lcms_profile (cmsCreate_sRGBProfile (), NULL));
g_assert (new_profile);
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.
*/
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 (gdk_icc_profile_new_from_lcms_profile (lcms_profile, NULL));
g_assert (new_profile);
g_once_init_leave (&srgb_linear_profile, new_profile);
}
return srgb_linear_profile;
}
/**
* gdk_icc_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.6
*/
GdkICCProfile *
gdk_icc_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_ICC_PROFILE,
NULL,
error,
"icc-profile", bytes,
NULL);
}
GdkICCProfile *
gdk_icc_profile_new_from_lcms_profile (cmsHPROFILE lcms_profile,
GError **error)
{
GdkICCProfile *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_ICC_PROFILE, NULL);
result->lcms_profile = lcms_profile;
result->icc_profile = g_bytes_new_take (data, size);
return result;
}
/**
* gdk_icc_profile_get_icc_profile: (attributes org.gtk.Method.get_property=icc-profile)
* @self: a `GdkICCProfile`
*
* Returns the serialized ICC profile of @self as %GBytes.
*
* Returns: (transfer none): the ICC profile
*
* Since: 4.6
*/
GBytes *
gdk_icc_profile_get_icc_profile (GdkICCProfile *self)
{
g_return_val_if_fail (GDK_IS_ICC_PROFILE (self), NULL);
return self->icc_profile;
}
cmsHPROFILE *
gdk_icc_profile_get_lcms_profile (GdkICCProfile *self)
{
g_return_val_if_fail (GDK_IS_ICC_PROFILE (self), NULL);
return self->lcms_profile;
}
typedef struct _GdkICCTransformCache GdkICCTransformCache;
struct _GdkICCTransformCache
{
GdkICCProfile *source;
guint source_type;
GdkICCProfile *dest;
guint dest_type;
};
static void
gdk_icc_transform_cache_free (gpointer data)
{
g_free (data);
}
static guint
gdk_icc_transform_cache_hash (gconstpointer data)
{
const GdkICCTransformCache *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_icc_transform_cache_equal (gconstpointer data1,
gconstpointer data2)
{
const GdkICCTransformCache *cache1 = data1;
const GdkICCTransformCache *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_icc_profile_lookup_transform (GdkICCProfile *source,
guint source_type,
GdkICCProfile *dest,
guint dest_type)
{
GdkICCTransformCache *entry;
static GHashTable *cache = NULL;
cmsHTRANSFORM *transform;
if (cache == NULL)
cache = g_hash_table_new_full (gdk_icc_transform_cache_hash,
gdk_icc_transform_cache_equal,
gdk_icc_transform_cache_free,
cmsDeleteTransform);
transform = g_hash_table_lookup (cache,
&(GdkICCTransformCache) {
source, source_type,
dest, dest_type
});
if (G_UNLIKELY (transform == NULL))
{
transform = cmsCreateTransform (gdk_icc_profile_get_lcms_profile (source),
source_type,
gdk_icc_profile_get_lcms_profile (dest),
dest_type,
INTENT_PERCEPTUAL,
cmsFLAGS_COPY_ALPHA);
entry = g_new (GdkICCTransformCache, 1);
*entry = (GdkICCTransformCache) {
source, source_type,
dest, dest_type
};
g_hash_table_insert (cache, entry, transform);
}
return transform;
}

56
gdk/gdkiccprofile.h Normal file
View File

@@ -0,0 +1,56 @@
/* gdkiccprofile.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_ICC_PROFILE_H__
#define __GDK_ICC_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/gdkcolorprofile.h>
G_BEGIN_DECLS
#define GDK_TYPE_ICC_PROFILE (gdk_icc_profile_get_type ())
#define GDK_ICC_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_ICC_PROFILE, GdkICCProfile))
#define GDK_IS_ICC_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_ICC_PROFILE))
#define GDK_ICC_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_ICC_PROFILE, GdkICCProfileClass))
#define GDK_IS_ICC_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_ICC_PROFILE))
#define GDK_ICC_PROFILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_ICC_PROFILE, GdkICCProfileClass))
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GdkICCProfile, g_object_unref)
typedef struct _GdkICCProfileClass GdkICCProfileClass;
GDK_AVAILABLE_IN_4_6
GType gdk_icc_profile_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_4_6
GdkICCProfile * gdk_icc_profile_new_from_icc_bytes (GBytes *bytes,
GError **error);
GDK_AVAILABLE_IN_4_6
GBytes * gdk_icc_profile_get_icc_profile (GdkICCProfile *self);
G_END_DECLS
#endif /* __GDK_ICC_PROFILE_H__ */

View File

@@ -0,0 +1,24 @@
#ifndef __GDK_ICC_PROFILE_PRIVATE_H__
#define __GDK_ICC_PROFILE_PRIVATE_H__
#include "gdkiccprofile.h"
#include "gdkmemorytexture.h"
#include <lcms2.h>
G_BEGIN_DECLS
GdkICCProfile * gdk_icc_profile_new_from_lcms_profile (cmsHPROFILE lcms_profile,
GError **error);
cmsHPROFILE * gdk_icc_profile_get_lcms_profile (GdkICCProfile *self);
cmsHTRANSFORM * gdk_icc_profile_lookup_transform (GdkICCProfile *source,
guint source_type,
GdkICCProfile *dest,
guint dest_type);
G_END_DECLS
#endif /* __GDK_COLOR_PROFILE_PRIVATE_H__ */

900
gdk/gdkmemoryformat.c Normal file
View File

@@ -0,0 +1,900 @@
/*
* Copyright © 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gdkmemoryformatprivate.h"
#include "gdkiccprofileprivate.h"
#include "gdkprofilerprivate.h"
#include "gsk/ngl/fp16private.h"
#include <epoxy/gl.h>
typedef struct _GdkMemoryFormatDescription GdkMemoryFormatDescription;
#define TYPED_FUNCS(name, T, R, G, B, A, bpp, scale) \
static void \
name ## _to_float (float *dest, \
const guchar *src_data, \
gsize n) \
{ \
for (gsize i = 0; i < n; i++) \
{ \
T *src = (T *) (src_data + i * bpp); \
dest[0] = (float) src[R] / scale; \
dest[1] = (float) src[G] / scale; \
dest[2] = (float) src[B] / scale; \
if (A >= 0) dest[3] = (float) src[A] / scale; else dest[3] = 1.0; \
dest += 4; \
} \
} \
\
static void \
name ## _from_float (guchar *dest_data, \
const float *src, \
gsize n) \
{ \
for (gsize i = 0; i < n; i++) \
{ \
T *dest = (T *) (dest_data + i * bpp); \
dest[R] = CLAMP (src[0] * (scale + 1), 0, scale); \
dest[G] = CLAMP (src[1] * (scale + 1), 0, scale); \
dest[B] = CLAMP (src[2] * (scale + 1), 0, scale); \
if (A >= 0) dest[A] = CLAMP (src[3] * (scale + 1), 0, scale); \
src += 4; \
} \
}
TYPED_FUNCS (b8g8r8a8_premultiplied, guchar, 2, 1, 0, 3, 4, 255)
TYPED_FUNCS (a8r8g8b8_premultiplied, guchar, 1, 2, 3, 0, 4, 255)
TYPED_FUNCS (r8g8b8a8_premultiplied, guchar, 0, 1, 2, 3, 4, 255)
TYPED_FUNCS (b8g8r8a8, guchar, 2, 1, 0, 3, 4, 255)
TYPED_FUNCS (a8r8g8b8, guchar, 1, 2, 3, 0, 4, 255)
TYPED_FUNCS (r8g8b8a8, guchar, 0, 1, 2, 3, 4, 255)
TYPED_FUNCS (a8b8g8r8, guchar, 3, 2, 1, 0, 4, 255)
TYPED_FUNCS (r8g8b8, guchar, 0, 1, 2, -1, 3, 255)
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,
gsize n)
{
guint16 *src = (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
r16g16b16_float_from_float (guchar *dest_data,
const float *src,
gsize n)
{
guint16 *dest = (guint16 *) dest_data;
for (gsize i = 0; i < n; i++)
{
float_to_half (src, dest, 3);
dest += 3;
src += 4;
}
}
static void
r16g16b16a16_float_to_float (float *dest,
const guchar *src,
gsize n)
{
half_to_float ((const guint16 *) src, dest, 4 * n);
}
static void
r16g16b16a16_float_from_float (guchar *dest,
const float *src,
gsize n)
{
float_to_half (src, (guint16 *) dest, 4 * n);
}
static void
r32g32b32_float_to_float (float *dest,
const guchar *src_data,
gsize n)
{
float *src = (float *) src_data;
for (gsize i = 0; i < n; i++)
{
dest[0] = src[0];
dest[1] = src[1];
dest[2] = src[2];
dest[3] = 1.0;
dest += 4;
src += 3;
}
}
static void
r32g32b32_float_from_float (guchar *dest_data,
const float *src,
gsize n)
{
float *dest = (float *) dest_data;
for (gsize i = 0; i < n; i++)
{
dest[0] = src[0];
dest[1] = src[1];
dest[2] = src[2];
dest += 3;
src += 4;
}
}
static void
r32g32b32a32_float_to_float (float *dest,
const guchar *src,
gsize n)
{
memcpy (dest, src, sizeof (float) * n * 4);
}
static void
r32g32b32a32_float_from_float (guchar *dest,
const float *src,
gsize n)
{
memcpy (dest, src, sizeof (float) * n * 4);
}
struct _GdkMemoryFormatDescription
{
const char *name;
GdkMemoryAlpha alpha;
gsize bytes_per_pixel;
gsize alignment;
gboolean supports_gles;
struct {
guint internal_format;
guint format;
guint type;
} gl;
/* 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
# define GDK_GL_UNSIGNED_BYTE_FLIPPED GL_UNSIGNED_INT_8_8_8_8
#elif G_BYTE_ORDER == G_BIG_ENDIAN
# define GDK_GL_UNSIGNED_BYTE_FLIPPED GL_UNSIGNED_INT_8_8_8_8_REV
#else
# error "Define the right GL flags here"
#endif
static const GdkMemoryFormatDescription memory_formats[GDK_MEMORY_N_FORMATS] = {
[GDK_MEMORY_B8G8R8A8_PREMULTIPLIED] = {
"B8G8R8A8_PREMULTIPLIED",
GDK_MEMORY_ALPHA_PREMULTIPLIED,
4,
G_ALIGNOF (guchar),
FALSE,
{ 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),
FALSE,
{ 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),
TRUE,
{ 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),
FALSE,
{ 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),
FALSE,
{ 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),
TRUE,
{ 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),
FALSE,
{ 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),
TRUE,
{ GL_RGBA8, 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),
FALSE,
{ 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),
TRUE,
{ 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),
TRUE,
{ 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_R16G16B16_FLOAT] = {
"R16G16B16_FLOAT",
GDK_MEMORY_ALPHA_OPAQUE,
6,
G_ALIGNOF (guint16),
TRUE,
{ 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),
TRUE,
{ 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_R32G32B32_FLOAT] = {
"R32G32B32_FLOAT",
GDK_MEMORY_ALPHA_OPAQUE,
12,
G_ALIGNOF (float),
TRUE,
{ 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),
TRUE,
{ GL_RGBA32F, GL_RGBA, GL_FLOAT },
r32g32b32a32_float_to_float,
r32g32b32a32_float_from_float,
{ TYPE_RGBA_FLT, 16 },
float_unpremultiply,
float_premultiply,
},
[GDK_MEMORY_R16G16B16A16] = {
"R16G16B16A16",
GDK_MEMORY_ALPHA_STRAIGHT,
8,
G_ALIGNOF (guint16),
TRUE,
{ GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT },
r16g16b16a16_to_float,
r16g16b16a16_from_float,
{ TYPE_RGBA_16, 8 },
NULL,
NULL,
},
[GDK_MEMORY_R16G16B16A16_FLOAT] = {
"R16G16B16A16_FLOAT",
GDK_MEMORY_ALPHA_STRAIGHT,
8,
G_ALIGNOF (guint16),
TRUE,
{ GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT },
r16g16b16a16_float_to_float,
r16g16b16a16_float_from_float,
{ TYPE_RGBA_HALF_FLT, 8 },
NULL,
NULL,
},
[GDK_MEMORY_R32G32B32A32_FLOAT] = {
"R32G32B32A32_FLOAT",
GDK_MEMORY_ALPHA_STRAIGHT,
16,
G_ALIGNOF (float),
TRUE,
{ GL_RGBA32F, GL_RGBA, GL_FLOAT },
r32g32b32a32_float_to_float,
r32g32b32a32_float_from_float,
{ TYPE_RGBA_FLT, 16 },
NULL,
NULL,
}
};
gsize
gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format)
{
return memory_formats[format].bytes_per_pixel;
}
GdkMemoryAlpha
gdk_memory_format_alpha (GdkMemoryFormat format)
{
return memory_formats[format].alpha;
}
gsize
gdk_memory_format_alignment (GdkMemoryFormat format)
{
return memory_formats[format].alignment;
}
gboolean
gdk_memory_format_gl_format (GdkMemoryFormat format,
gboolean gles,
guint *out_internal_format,
guint *out_format,
guint *out_type)
{
*out_internal_format = memory_formats[format].gl.internal_format;
*out_format = memory_formats[format].gl.format;
*out_type = memory_formats[format].gl.type;
if (memory_formats[format].alpha == GDK_MEMORY_ALPHA_STRAIGHT)
return FALSE;
if (gles && !memory_formats[format].supports_gles)
return FALSE;
return TRUE;
}
gboolean
gdk_memory_format_is_hdr (GdkMemoryFormat format)
{
switch (memory_formats[format].gl.type)
{
case GL_FLOAT:
case GL_HALF_FLOAT:
case GL_UNSIGNED_SHORT:
return TRUE;
default:
return FALSE;
}
}
static void
premultiply (float *rgba,
gsize n)
{
for (gsize i = 0; i < n; i++)
{
rgba[0] *= rgba[3];
rgba[1] *= rgba[3];
rgba[2] *= rgba[3];
rgba += 4;
}
}
static void
unpremultiply (float *rgba,
gsize n)
{
for (gsize i = 0; i < n; i++)
{
if (rgba[3] > 1/255.0)
{
rgba[0] /= rgba[3];
rgba[1] /= rgba[3];
rgba[2] /= rgba[3];
}
rgba += 4;
}
}
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 *desc = &memory_formats[format];
gsize y, stride;
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++)
{
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;
}
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;
g_assert (GDK_IS_ICC_PROFILE (src_profile));
g_assert (GDK_IS_ICC_PROFILE (dest_profile));
transform = gdk_icc_profile_lookup_transform (GDK_ICC_PROFILE (src_profile),
src_desc->lcms.type,
GDK_ICC_PROFILE (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++)
{
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 (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

@@ -0,0 +1,57 @@
/*
* Copyright © 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.1 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GDK_MEMORY_CONVERT_PRIVATE_H__
#define __GDK_MEMORY_CONVERT_PRIVATE_H__
#include "gdkmemorytexture.h"
G_BEGIN_DECLS
typedef enum {
GDK_MEMORY_ALPHA_PREMULTIPLIED,
GDK_MEMORY_ALPHA_STRAIGHT,
GDK_MEMORY_ALPHA_OPAQUE
} GdkMemoryAlpha;
gsize gdk_memory_format_alignment (GdkMemoryFormat format) G_GNUC_CONST;
GdkMemoryAlpha gdk_memory_format_alpha (GdkMemoryFormat format) G_GNUC_CONST;
gsize gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format) G_GNUC_CONST;
gboolean gdk_memory_format_gl_format (GdkMemoryFormat format,
gboolean gles,
guint *out_internal_format,
guint *out_format,
guint *out_type);
gboolean gdk_memory_format_is_hdr (GdkMemoryFormat format);
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_END_DECLS
#endif /* __GDK_MEMORY_CONVERT_PRIVATE_H__ */

View File

@@ -20,6 +20,9 @@
#include "config.h"
#include "gdkmemorytextureprivate.h"
#include "gdkcolorprofileprivate.h"
#include "gdkmemoryformatprivate.h"
#include "gsk/ngl/fp16private.h"
/**
@@ -45,80 +48,6 @@ struct _GdkMemoryTextureClass
G_DEFINE_TYPE (GdkMemoryTexture, gdk_memory_texture, GDK_TYPE_TEXTURE)
gsize
gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format)
{
switch (format)
{
case GDK_MEMORY_R8G8B8:
case GDK_MEMORY_B8G8R8:
return 3;
case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED:
case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED:
case GDK_MEMORY_B8G8R8A8:
case GDK_MEMORY_A8R8G8B8:
case GDK_MEMORY_R8G8B8A8:
case GDK_MEMORY_A8B8G8R8:
return 4;
case GDK_MEMORY_R16G16B16:
case GDK_MEMORY_R16G16B16_FLOAT:
return 6;
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
return 8;
case GDK_MEMORY_R32G32B32_FLOAT:
return 12;
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
return 16;
case GDK_MEMORY_N_FORMATS:
default:
g_assert_not_reached ();
return 4;
}
}
static gsize
gdk_memory_format_alignment (GdkMemoryFormat format)
{
switch (format)
{
case GDK_MEMORY_R8G8B8:
case GDK_MEMORY_B8G8R8:
case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED:
case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED:
case GDK_MEMORY_B8G8R8A8:
case GDK_MEMORY_A8R8G8B8:
case GDK_MEMORY_R8G8B8A8:
case GDK_MEMORY_A8B8G8R8:
return G_ALIGNOF (guchar);
case GDK_MEMORY_R16G16B16:
case GDK_MEMORY_R16G16B16_FLOAT:
return G_ALIGNOF (guint16);
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
return G_ALIGNOF (guint16);
case GDK_MEMORY_R32G32B32_FLOAT:
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
return G_ALIGNOF (float);
case GDK_MEMORY_N_FORMATS:
default:
g_assert_not_reached ();
return G_ALIGNOF (double);
}
}
static void
gdk_memory_texture_dispose (GObject *object)
{
@@ -143,10 +72,12 @@ gdk_memory_texture_download (GdkTexture *texture,
GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (texture);
gdk_memory_convert (data, stride,
GDK_MEMORY_CONVERT_DOWNLOAD,
GDK_MEMORY_DEFAULT,
gdk_color_profile_get_srgb (),
(guchar *) g_bytes_get_data (self->bytes, NULL),
self->stride,
self->format,
gdk_texture_get_color_profile (texture),
gdk_texture_get_width (texture),
gdk_texture_get_height (texture));
}
@@ -158,12 +89,24 @@ gdk_memory_texture_download_float (GdkTexture *texture,
{
GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (texture);
gdk_memory_convert_to_float (data, stride,
(guchar *) g_bytes_get_data (self->bytes, NULL),
self->stride,
self->format,
gdk_texture_get_width (texture),
gdk_texture_get_height (texture));
gdk_memory_convert ((guchar *) data,
stride * sizeof (float),
GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED,
gdk_color_profile_get_srgb (),
(guchar *) g_bytes_get_data (self->bytes, NULL),
self->stride,
self->format,
gdk_texture_get_color_profile (texture),
gdk_texture_get_width (texture),
gdk_texture_get_height (texture));
}
static gboolean
gdk_memory_texture_is_hdr (GdkTexture *texture)
{
GdkMemoryTexture *self = GDK_MEMORY_TEXTURE (texture);
return gdk_memory_format_is_hdr (self->format);
}
static void
@@ -175,6 +118,7 @@ gdk_memory_texture_class_init (GdkMemoryTextureClass *klass)
texture_class->download_texture = gdk_memory_texture_download_texture;
texture_class->download = gdk_memory_texture_download;
texture_class->download_float = gdk_memory_texture_download_float;
texture_class->is_hdr = gdk_memory_texture_is_hdr;
gobject_class->dispose = gdk_memory_texture_dispose;
}
@@ -231,6 +175,9 @@ gdk_memory_sanitize (GBytes *bytes,
* The `GBytes` must contain @stride x @height pixels
* in the given format.
*
* This function calls [ctor@Gdk.MemoryTexture.new_with_color_profile]
* with the sRGB profile.
*
* Returns: A newly-created `GdkTexture`
*/
GdkTexture *
@@ -239,11 +186,49 @@ gdk_memory_texture_new (int width,
GdkMemoryFormat format,
GBytes *bytes,
gsize stride)
{
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
* @color_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.6
*/
GdkTexture *
gdk_memory_texture_new_with_color_profile (int width,
int height,
GdkMemoryFormat format,
GdkColorProfile *color_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 (color_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);
@@ -252,6 +237,7 @@ gdk_memory_texture_new (int width,
self = g_object_new (GDK_TYPE_MEMORY_TEXTURE,
"width", width,
"height", height,
"color-profile", color_profile,
NULL);
self->format = format;
@@ -261,12 +247,88 @@ gdk_memory_texture_new (int width,
return GDK_TEXTURE (self);
}
GdkMemoryTexture *
gdk_memory_texture_convert (GdkMemoryTexture *source,
GdkMemoryFormat to_format,
GdkColorProfile *to_profile,
const GdkRectangle *rect_or_null)
{
GdkTexture *texture = GDK_TEXTURE (source);
GdkTexture *result;
GBytes *to_bytes;
int width, height;
gsize offset, stride;
width = texture->width;
height = texture->height;
if (rect_or_null)
{
g_assert (rect_or_null->x + rect_or_null->width <= width);
g_assert (rect_or_null->y + rect_or_null->height <= height);
offset = rect_or_null->y * source->stride +
rect_or_null->x * gdk_memory_format_bytes_per_pixel (source->format);
width = rect_or_null->width;
height = rect_or_null->height;
}
else
offset = 0;
if (to_format == source->format &&
gdk_color_profile_equal (texture->color_profile, to_profile))
{
if (offset == 0 && width == texture->width && height == texture->height)
return source;
to_bytes = g_bytes_new_from_bytes (source->bytes,
offset,
source->stride * height);
stride = source->stride;
}
else
{
guchar *data;
stride = gdk_memory_format_bytes_per_pixel (to_format) * width;
data = g_malloc_n (stride, height);
gdk_memory_convert (data,
stride,
to_format,
to_profile,
(guchar *) g_bytes_get_data (source->bytes, NULL) + offset,
source->stride,
source->format,
texture->color_profile,
width,
height);
to_bytes = g_bytes_new_take (data, stride * height);
}
result = gdk_memory_texture_new_with_color_profile (width,
height,
to_format,
to_profile,
to_bytes,
stride);
g_bytes_unref (to_bytes);
g_object_unref (source);
return GDK_MEMORY_TEXTURE (result);
}
GdkMemoryFormat
gdk_memory_texture_get_format (GdkMemoryTexture *self)
{
return self->format;
}
GBytes *
gdk_memory_texture_get_bytes (GdkMemoryTexture *self)
{
return self->bytes;
}
const guchar *
gdk_memory_texture_get_data (GdkMemoryTexture *self)
{
@@ -279,429 +341,3 @@ gdk_memory_texture_get_stride (GdkMemoryTexture *self)
return self->stride;
}
static void
convert_memcpy (guchar *dest_data,
gsize dest_stride,
const guchar *src_data,
gsize src_stride,
gsize width,
gsize height)
{
gsize y;
for (y = 0; y < height; y++)
memcpy (dest_data + y * dest_stride, src_data + y * src_stride, 4 * width);
}
#define SWIZZLE(A,R,G,B) \
static void \
convert_swizzle ## A ## R ## G ## B (guchar *dest_data, \
gsize dest_stride, \
const guchar *src_data, \
gsize src_stride, \
gsize width, \
gsize height) \
{ \
gsize x, y; \
\
for (y = 0; y < height; y++) \
{ \
for (x = 0; x < width; x++) \
{ \
dest_data[4 * x + A] = src_data[4 * x + 0]; \
dest_data[4 * x + R] = src_data[4 * x + 1]; \
dest_data[4 * x + G] = src_data[4 * x + 2]; \
dest_data[4 * x + B] = src_data[4 * x + 3]; \
} \
\
dest_data += dest_stride; \
src_data += src_stride; \
} \
}
SWIZZLE(3,2,1,0)
SWIZZLE(2,1,0,3)
SWIZZLE(3,0,1,2)
SWIZZLE(1,2,3,0)
#define SWIZZLE_OPAQUE(A,R,G,B) \
static void \
convert_swizzle_opaque_## A ## R ## G ## B (guchar *dest_data, \
gsize dest_stride, \
const guchar *src_data, \
gsize src_stride, \
gsize width, \
gsize height) \
{ \
gsize x, y; \
\
for (y = 0; y < height; y++) \
{ \
for (x = 0; x < width; x++) \
{ \
dest_data[4 * x + A] = 0xFF; \
dest_data[4 * x + R] = src_data[3 * x + 0]; \
dest_data[4 * x + G] = src_data[3 * x + 1]; \
dest_data[4 * x + B] = src_data[3 * x + 2]; \
} \
\
dest_data += dest_stride; \
src_data += src_stride; \
} \
}
SWIZZLE_OPAQUE(3,2,1,0)
SWIZZLE_OPAQUE(3,0,1,2)
SWIZZLE_OPAQUE(0,1,2,3)
SWIZZLE_OPAQUE(0,3,2,1)
#define PREMULTIPLY(d,c,a) G_STMT_START { guint t = c * a + 0x80; d = ((t >> 8) + t) >> 8; } G_STMT_END
#define SWIZZLE_PREMULTIPLY(A,R,G,B, A2,R2,G2,B2) \
static void \
convert_swizzle_premultiply_ ## A ## R ## G ## B ## _ ## A2 ## R2 ## G2 ## B2 \
(guchar *dest_data, \
gsize dest_stride, \
const guchar *src_data, \
gsize src_stride, \
gsize width, \
gsize height) \
{ \
gsize x, y; \
\
for (y = 0; y < height; y++) \
{ \
for (x = 0; x < width; x++) \
{ \
dest_data[4 * x + A] = src_data[4 * x + A2]; \
PREMULTIPLY(dest_data[4 * x + R], src_data[4 * x + R2], src_data[4 * x + A2]); \
PREMULTIPLY(dest_data[4 * x + G], src_data[4 * x + G2], src_data[4 * x + A2]); \
PREMULTIPLY(dest_data[4 * x + B], src_data[4 * x + B2], src_data[4 * x + A2]); \
} \
\
dest_data += dest_stride; \
src_data += src_stride; \
} \
}
SWIZZLE_PREMULTIPLY (3,2,1,0, 3,2,1,0)
SWIZZLE_PREMULTIPLY (0,1,2,3, 3,2,1,0)
SWIZZLE_PREMULTIPLY (3,2,1,0, 0,1,2,3)
SWIZZLE_PREMULTIPLY (0,1,2,3, 0,1,2,3)
SWIZZLE_PREMULTIPLY (3,2,1,0, 3,0,1,2)
SWIZZLE_PREMULTIPLY (0,1,2,3, 3,0,1,2)
SWIZZLE_PREMULTIPLY (3,2,1,0, 0,3,2,1)
SWIZZLE_PREMULTIPLY (0,1,2,3, 0,3,2,1)
SWIZZLE_PREMULTIPLY (3,0,1,2, 3,2,1,0)
SWIZZLE_PREMULTIPLY (3,0,1,2, 0,1,2,3)
SWIZZLE_PREMULTIPLY (3,0,1,2, 3,0,1,2)
SWIZZLE_PREMULTIPLY (3,0,1,2, 0,3,2,1)
#define CONVERT_FUNC(name,suffix,R,G,B,A,step) \
static void \
convert_ ## name ## _to_ ## suffix (guchar *dest_data, \
gsize dest_stride, \
const guchar *src_data, \
gsize src_stride, \
gsize width, \
gsize height) \
{ \
gsize x, y; \
\
for (y = 0; y < height; y++) \
{ \
for (x = 0; x < width; x++) \
{ \
guchar conv[4]; \
convert_pixel_ ## name (conv, src_data + step * x); \
dest_data[4 * x + R] = conv[0]; \
dest_data[4 * x + G] = conv[1]; \
dest_data[4 * x + B] = conv[2]; \
dest_data[4 * x + A] = conv[3]; \
} \
\
dest_data += dest_stride; \
src_data += src_stride; \
} \
}
#define CONVERT_FUNCS(name,step) \
CONVERT_FUNC(name, download_le, 2, 1, 0, 3, step) \
CONVERT_FUNC(name, download_be, 1, 2, 3, 0, step) \
CONVERT_FUNC(name, gles_rgba, 0, 1, 2, 3, step) \
static inline void
convert_pixel_rgb16 (guchar *dest_data, const guchar *src_data)
{
const guint16 *src = (const guint16 *) src_data;
dest_data[0] = (guchar)(src[0] >> 8);
dest_data[1] = (guchar)(src[1] >> 8);
dest_data[2] = (guchar)(src[2] >> 8);
dest_data[3] = 0xFF;
}
CONVERT_FUNCS(rgb16, 3 * sizeof (guint16))
static inline void
convert_pixel_rgba16 (guchar *dest_data, const guchar *src_data)
{
const guint16 *src = (const guint16 *) src_data;
dest_data[0] = (guchar)(src[0] >> 8);
dest_data[1] = (guchar)(src[1] >> 8);
dest_data[2] = (guchar)(src[2] >> 8);
dest_data[3] = (guchar)(src[3] >> 8);
}
CONVERT_FUNCS(rgba16, 4 * sizeof (guint16))
static inline void
convert_pixel_rgb16f (guchar *dest_data, const guchar *src_data)
{
float src[4];
guint16 tmp[4];
memcpy(tmp, src_data, sizeof(guint16) * 3);
half_to_float4(tmp, src);
dest_data[0] = CLAMP (src[0] * 256.f, 0.f, 255.f);
dest_data[1] = CLAMP (src[1] * 256.f, 0.f, 255.f);
dest_data[2] = CLAMP (src[2] * 256.f, 0.f, 255.f);
dest_data[3] = 0xFF;
}
CONVERT_FUNCS(rgb16f, 3 * sizeof (guint16))
static inline void
convert_pixel_rgba16f (guchar *dest_data, const guchar *src_data)
{
float src[4];
half_to_float4((const guint16 *) src_data, src);
dest_data[0] = CLAMP (src[0] * 256.f, 0.f, 255.f);
dest_data[1] = CLAMP (src[1] * 256.f, 0.f, 255.f);
dest_data[2] = CLAMP (src[2] * 256.f, 0.f, 255.f);
dest_data[3] = CLAMP (src[3] * 256.f, 0.f, 255.f);
}
CONVERT_FUNCS(rgba16f, 4 * sizeof (guint16))
static inline void
convert_pixel_rgb32f (guchar *dest_data, const guchar *src_data)
{
float *src = (float *) src_data;
dest_data[0] = CLAMP (src[0] * 256.f, 0.f, 255.f);
dest_data[1] = CLAMP (src[1] * 256.f, 0.f, 255.f);
dest_data[2] = CLAMP (src[2] * 256.f, 0.f, 255.f);
dest_data[3] = 0xFF;
}
CONVERT_FUNCS(rgb32f, 3 * sizeof (float))
static inline void
convert_pixel_rgba32f (guchar *dest_data, const guchar *src_data)
{
float *src = (float *) src_data;
dest_data[0] = CLAMP (src[0] * 256.f, 0.f, 255.f);
dest_data[1] = CLAMP (src[1] * 256.f, 0.f, 255.f);
dest_data[2] = CLAMP (src[2] * 256.f, 0.f, 255.f);
dest_data[3] = CLAMP (src[3] * 256.f, 0.f, 255.f);
}
CONVERT_FUNCS(rgba32f, 4 * sizeof (float))
typedef void (* ConversionFunc) (guchar *dest_data,
gsize dest_stride,
const guchar *src_data,
gsize src_stride,
gsize width,
gsize height);
static ConversionFunc converters[GDK_MEMORY_N_FORMATS][GDK_MEMORY_N_CONVERSIONS] =
{
{ convert_memcpy, convert_swizzle3210, convert_swizzle2103 },
{ convert_swizzle3210, convert_memcpy, convert_swizzle3012 },
{ convert_swizzle2103, convert_swizzle1230, convert_memcpy },
{ convert_swizzle_premultiply_3210_3210, convert_swizzle_premultiply_0123_3210, convert_swizzle_premultiply_3012_3210, },
{ convert_swizzle_premultiply_3210_0123, convert_swizzle_premultiply_0123_0123, convert_swizzle_premultiply_3012_0123 },
{ convert_swizzle_premultiply_3210_3012, convert_swizzle_premultiply_0123_3012, convert_swizzle_premultiply_3012_3012 },
{ convert_swizzle_premultiply_3210_0321, convert_swizzle_premultiply_0123_0321, convert_swizzle_premultiply_3012_0321 },
{ convert_swizzle_opaque_3210, convert_swizzle_opaque_0123, convert_swizzle_opaque_3012 },
{ convert_swizzle_opaque_3012, convert_swizzle_opaque_0321, convert_swizzle_opaque_3210 },
{ convert_rgb16_to_download_le, convert_rgb16_to_download_be, convert_rgb16_to_gles_rgba },
{ convert_rgba16_to_download_le, convert_rgba16_to_download_be, convert_rgba16_to_gles_rgba },
{ convert_rgb16f_to_download_le, convert_rgb16f_to_download_be, convert_rgb16f_to_gles_rgba },
{ convert_rgba16f_to_download_le, convert_rgba16f_to_download_be, convert_rgba16f_to_gles_rgba },
{ convert_rgb32f_to_download_le, convert_rgb32f_to_download_be, convert_rgb32f_to_gles_rgba },
{ convert_rgba32f_to_download_le, convert_rgba32f_to_download_be, convert_rgba32f_to_gles_rgba }
};
void
gdk_memory_convert (guchar *dest_data,
gsize dest_stride,
GdkMemoryConversion dest_format,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
gsize width,
gsize height)
{
g_assert (dest_format < 3);
g_assert (src_format < GDK_MEMORY_N_FORMATS);
converters[src_format][dest_format] (dest_data, dest_stride, src_data, src_stride, width, height);
}
#define CONVERT_FLOAT(R,G,B,A,premultiply) G_STMT_START {\
for (y = 0; y < height; y++) \
{ \
for (x = 0; x < width; x++) \
{ \
if (A >= 0) \
{ \
dest_data[4 * x + 0] = src_data[4 * x + R] / 255.0f; \
dest_data[4 * x + 1] = src_data[4 * x + G] / 255.0f; \
dest_data[4 * x + 2] = src_data[4 * x + B] / 255.0f; \
dest_data[4 * x + 3] = src_data[4 * x + A] / 255.0f; \
if (premultiply) \
{ \
dest_data[4 * x + 0] *= dest_data[4 * x + 3]; \
dest_data[4 * x + 1] *= dest_data[4 * x + 3]; \
dest_data[4 * x + 2] *= dest_data[4 * x + 3]; \
} \
} \
else \
{ \
dest_data[4 * x + 0] = src_data[3 * x + R] / 255.0f; \
dest_data[4 * x + 1] = src_data[3 * x + G] / 255.0f; \
dest_data[4 * x + 2] = src_data[3 * x + B] / 255.0f; \
dest_data[4 * x + 3] = 1.0; \
} \
} \
\
dest_data += dest_stride; \
src_data += src_stride; \
} \
}G_STMT_END
#define CONVERT_FLOAT_PIXEL(func,step) G_STMT_START{\
for (y = 0; y < height; y++) \
{ \
for (x = 0; x < width; x++) \
{ \
func (dest_data + 4 * x, src_data + step * x); \
} \
\
dest_data += dest_stride; \
src_data += src_stride; \
} \
}G_STMT_END
static inline void
convert_rgb16_to_float (float *dest, const guchar *src_data)
{
const guint16 *src = (const guint16 *) src_data;
dest[0] = src[0] / 65535.f;
dest[1] = src[1] / 65535.f;
dest[2] = src[2] / 65535.f;
dest[3] = 1.0;
}
static inline void
convert_rgba16_to_float (float *dest, const guchar *src_data)
{
const guint16 *src = (const guint16 *) src_data;
dest[0] = src[0] / 65535.f;
dest[1] = src[1] / 65535.f;
dest[2] = src[2] / 65535.f;
dest[3] = src[3] / 65535.f;
}
static inline void
convert_rgb16f_to_float (float *dest, const guchar *src_data)
{
guint16 tmp[4];
memcpy(tmp, src_data, sizeof(guint16) * 3);
tmp[3] = FP16_ONE;
half_to_float4 (tmp, dest);
}
static inline void
convert_rgba16f_to_float (float *dest, const guchar *src_data)
{
half_to_float4 ((const guint16 *) src_data, dest);
}
static inline void
convert_rgb32f_to_float (float *dest, const guchar *src_data)
{
const float *src = (const float *) src_data;
dest[0] = src[0];
dest[1] = src[1];
dest[2] = src[2];
dest[3] = 1.0;
}
static inline void
convert_rgba32f_to_float (float *dest, const guchar *src_data)
{
const float *src = (const float *) src_data;
dest[0] = src[0];
dest[1] = src[1];
dest[2] = src[2];
dest[3] = src[3];
}
void
gdk_memory_convert_to_float (float *dest_data,
gsize dest_stride,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
gsize width,
gsize height)
{
gsize x, y;
switch (src_format)
{
case GDK_MEMORY_B8G8R8A8_PREMULTIPLIED:
CONVERT_FLOAT (2, 1, 0, 3, FALSE);
break;
case GDK_MEMORY_A8R8G8B8_PREMULTIPLIED:
CONVERT_FLOAT (1, 2, 3, 0, FALSE);
break;
case GDK_MEMORY_R8G8B8A8_PREMULTIPLIED:
CONVERT_FLOAT (0, 1, 2, 3, FALSE);
break;
case GDK_MEMORY_B8G8R8A8:
CONVERT_FLOAT (2, 1, 0, 3, TRUE);
break;
case GDK_MEMORY_A8R8G8B8:
CONVERT_FLOAT (1, 2, 3, 0, TRUE);
break;
case GDK_MEMORY_R8G8B8A8:
CONVERT_FLOAT (0, 1, 2, 3, TRUE);
break;
case GDK_MEMORY_A8B8G8R8:
CONVERT_FLOAT (3, 2, 1, 0, TRUE);
break;
case GDK_MEMORY_R8G8B8:
CONVERT_FLOAT (0, 1, 2, -1, FALSE);
break;
case GDK_MEMORY_B8G8R8:
CONVERT_FLOAT (2, 1, 0, -1, FALSE);
break;
case GDK_MEMORY_R16G16B16:
CONVERT_FLOAT_PIXEL (convert_rgb16_to_float, 3 * sizeof (guint16));
break;
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
CONVERT_FLOAT_PIXEL (convert_rgba16_to_float, 4 * sizeof (guint16));
break;
case GDK_MEMORY_R16G16B16_FLOAT:
CONVERT_FLOAT_PIXEL (convert_rgb16f_to_float, 3 * sizeof (guint16));
break;
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
CONVERT_FLOAT_PIXEL (convert_rgba16f_to_float, 4 * sizeof (guint16));
break;
case GDK_MEMORY_R32G32B32_FLOAT:
CONVERT_FLOAT_PIXEL (convert_rgb32f_to_float, 3 * sizeof (float));
break;
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
CONVERT_FLOAT_PIXEL (convert_rgba32f_to_float, 4 * sizeof (float));
break;
case GDK_MEMORY_N_FORMATS:
default:
g_assert_not_reached();
}
}

View File

@@ -87,6 +87,11 @@ typedef enum {
GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED,
GDK_MEMORY_R32G32B32_FLOAT,
GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED,
#ifdef GTK_COMPILATION
GDK_MEMORY_R16G16B16A16,
GDK_MEMORY_R16G16B16A16_FLOAT,
GDK_MEMORY_R32G32B32A32_FLOAT,
#endif
GDK_MEMORY_N_FORMATS
} GdkMemoryFormat;
@@ -130,6 +135,14 @@ GdkTexture * gdk_memory_texture_new (int
GdkMemoryFormat format,
GBytes *bytes,
gsize stride);
GDK_AVAILABLE_IN_4_6
GdkTexture * gdk_memory_texture_new_with_color_profile
(int width,
int height,
GdkMemoryFormat format,
GdkColorProfile *color_profile,
GBytes *bytes,
gsize stride);
G_END_DECLS

View File

@@ -29,44 +29,15 @@ G_BEGIN_DECLS
#define GDK_MEMORY_GDK_PIXBUF_OPAQUE GDK_MEMORY_R8G8B8
#define GDK_MEMORY_GDK_PIXBUF_ALPHA GDK_MEMORY_R8G8B8A8
typedef enum {
GDK_MEMORY_CONVERT_DOWNLOAD_LITTLE_ENDIAN,
GDK_MEMORY_CONVERT_DOWNLOAD_BIT_ENDIAN,
GDK_MEMORY_CONVERT_GLES_RGBA,
GdkMemoryTexture * gdk_memory_texture_convert (GdkMemoryTexture *source,
GdkMemoryFormat to_format,
GdkColorProfile *to_profile,
const GdkRectangle *rect_or_null);
GDK_MEMORY_N_CONVERSIONS
} GdkMemoryConversion;
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define GDK_MEMORY_CONVERT_DOWNLOAD GDK_MEMORY_CONVERT_DOWNLOAD_LITTLE_ENDIAN
#elif G_BYTE_ORDER == G_BIG_ENDIAN
#define GDK_MEMORY_CONVERT_DOWNLOAD GDK_MEMORY_CONVERT_DOWNLOAD_BIG_ENDIAN
#else
#error "Unknown byte order for GDK_MEMORY_CONVERT_DOWNLOAD"
#endif
gsize gdk_memory_format_bytes_per_pixel (GdkMemoryFormat format);
GdkMemoryFormat gdk_memory_texture_get_format (GdkMemoryTexture *self);
const guchar * gdk_memory_texture_get_data (GdkMemoryTexture *self);
gsize gdk_memory_texture_get_stride (GdkMemoryTexture *self);
void gdk_memory_convert (guchar *dest_data,
gsize dest_stride,
GdkMemoryConversion dest_format,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
gsize width,
gsize height);
void gdk_memory_convert_to_float (float *dest_data,
gsize dest_stride,
const guchar *src_data,
gsize src_stride,
GdkMemoryFormat src_format,
gsize width,
gsize height);
GdkMemoryFormat gdk_memory_texture_get_format (GdkMemoryTexture *self);
GBytes * gdk_memory_texture_get_bytes (GdkMemoryTexture *self);
const guchar * gdk_memory_texture_get_data (GdkMemoryTexture *self);
gsize gdk_memory_texture_get_stride (GdkMemoryTexture *self);
G_END_DECLS

View File

@@ -24,6 +24,9 @@
#include "gdkpixbuf.h"
#include "gdkcolorprofile.h"
#include "gdkmemoryformatprivate.h"
#include "gdkmemorytextureprivate.h"
#include "gdksurface.h"
#include "gdktextureprivate.h"
@@ -214,6 +217,13 @@ gdk_pixbuf_get_from_surface (cairo_surface_t *surface,
return dest;
}
static void
pixbuf_texture_unref_cb (guchar *pixels,
gpointer texture)
{
g_object_unref (texture);
}
/**
* gdk_pixbuf_get_from_texture:
* @texture: a `GdkTexture`
@@ -229,17 +239,24 @@ gdk_pixbuf_get_from_surface (cairo_surface_t *surface,
GdkPixbuf *
gdk_pixbuf_get_from_texture (GdkTexture *texture)
{
GdkPixbuf *pixbuf;
cairo_surface_t *surface;
int width, height;
GdkMemoryTexture *memtex;
gboolean alpha;
g_return_val_if_fail (GDK_IS_TEXTURE (texture), NULL);
memtex = GDK_MEMORY_TEXTURE (gdk_texture_download_texture (texture));
alpha = gdk_memory_format_alpha (gdk_memory_texture_get_format (memtex)) != GDK_MEMORY_ALPHA_OPAQUE;
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
surface = gdk_texture_download_surface (texture);
pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, width, height);
cairo_surface_destroy (surface);
return pixbuf;
memtex = gdk_memory_texture_convert (memtex,
alpha ? GDK_MEMORY_GDK_PIXBUF_ALPHA
: GDK_MEMORY_GDK_PIXBUF_OPAQUE,
gdk_color_profile_get_srgb (),
NULL);
return gdk_pixbuf_new_from_data (gdk_memory_texture_get_data (memtex),
GDK_COLORSPACE_RGB,
alpha,
8,
gdk_texture_get_width (GDK_TEXTURE (memtex)),
gdk_texture_get_height (GDK_TEXTURE (memtex)),
gdk_memory_texture_get_stride (memtex),
pixbuf_texture_unref_cb,
memtex);
}

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,10 +488,18 @@ 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);
}
static void
gdk_surface_real_set_hdr (GdkSurface *surface,
gboolean hdr)
{
}
static void
gdk_surface_class_init (GdkSurfaceClass *klass)
{
@@ -499,6 +510,25 @@ gdk_surface_class_init (GdkSurfaceClass *klass)
object_class->get_property = gdk_surface_get_property;
klass->beep = gdk_surface_real_beep;
klass->set_hdr = gdk_surface_real_set_hdr;
/**
* 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.
*/
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)
@@ -729,6 +759,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);
@@ -783,6 +814,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;
@@ -2049,6 +2084,38 @@ 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
*/
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`
@@ -2361,7 +2428,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)
@@ -2377,6 +2444,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;
}
@@ -3063,3 +3131,11 @@ gdk_surface_leave_monitor (GdkSurface *surface,
{
g_signal_emit (surface, signals[LEAVE_MONITOR], 0, monitor);
}
void
gdk_surface_set_hdr (GdkSurface *surface,
gboolean hdr)
{
GDK_SURFACE_GET_CLASS (surface)->set_hdr (surface, hdr);
}

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_6
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;
@@ -160,6 +161,9 @@ struct _GdkSurfaceClass
cairo_region_t *region);
void (* request_layout) (GdkSurface *surface);
gboolean (* compute_size) (GdkSurface *surface);
void (* set_hdr) (GdkSurface *surface,
gboolean hdr);
};
#define GDK_SURFACE_DESTROYED(d) (((GdkSurface *)(d))->destroyed)
@@ -171,6 +175,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,
@@ -344,6 +350,8 @@ void gdk_surface_request_compute_size (GdkSurface *surface);
GDK_AVAILABLE_IN_ALL
void gdk_surface_request_motion (GdkSurface *surface);
void gdk_surface_set_hdr (GdkSurface *surface,
gboolean hdr);
G_END_DECLS

View File

@@ -40,7 +40,10 @@
#include "gdktextureprivate.h"
#include "gdkcairo.h"
#include "gdkiccprofile.h"
#include "gdkintl.h"
#include "gdkmemoryformatprivate.h"
#include "gdkmemorytextureprivate.h"
#include "gdkpaintable.h"
#include "gdksnapshot.h"
@@ -60,8 +63,9 @@ gtk_snapshot_append_texture (GdkSnapshot *snapshot,
enum {
PROP_0,
PROP_WIDTH,
PROP_COLOR_PROFILE,
PROP_HEIGHT,
PROP_WIDTH,
N_PROPS
};
@@ -262,14 +266,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;
@@ -286,14 +296,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;
@@ -306,10 +320,17 @@ 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);
}
static gboolean
gdk_texture_real_is_hdr (GdkTexture *texture)
{
return FALSE;
}
static void
gdk_texture_class_init (GdkTextureClass *klass)
{
@@ -318,20 +339,38 @@ gdk_texture_class_init (GdkTextureClass *klass)
klass->download_texture = gdk_texture_real_download_texture;
klass->download = gdk_texture_real_download;
klass->download_float = gdk_texture_real_download_float;
klass->is_hdr = gdk_texture_real_is_hdr;
gobject_class->set_property = gdk_texture_set_property;
gobject_class->get_property = gdk_texture_get_property;
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.6
*/
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,
@@ -341,14 +380,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,
@@ -391,17 +430,42 @@ gdk_texture_new_for_surface (cairo_surface_t *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 (gdk_icc_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`
@@ -419,24 +483,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;
}
@@ -692,23 +763,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.6
*/
GdkColorProfile *
gdk_texture_get_color_profile (GdkTexture *texture)
{
g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
return texture->color_profile;
}
/*<private>
* gdk_texture_download_surface:
* @texture: the texture to download
* @target_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 *target_profile)
{
cairo_surface_t *surface;
cairo_status_t surface_status;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
texture->width, texture->height);
if (target_profile != NULL)
gdk_cairo_surface_set_color_profile (surface, target_profile);
else
target_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
{
GdkTexture *download = gdk_texture_download_texture (texture);
GdkMemoryTexture *memdownload = GDK_MEMORY_TEXTURE (download);
gdk_memory_convert (cairo_image_surface_get_data (surface),
cairo_image_surface_get_stride (surface),
GDK_MEMORY_DEFAULT,
target_profile,
gdk_memory_texture_get_data (memdownload),
gdk_memory_texture_get_stride (memdownload),
gdk_memory_texture_get_format (memdownload),
download->color_profile,
download->width,
download->height);
g_object_unref (download);
}
gdk_texture_download (texture,
cairo_image_surface_get_data (surface),
cairo_image_surface_get_stride (surface));
cairo_surface_mark_dirty (surface);
return surface;
@@ -973,3 +1094,10 @@ gdk_texture_save_to_tiff_bytes (GdkTexture *texture)
return gdk_save_tiff (texture);
}
gboolean
gdk_texture_is_hdr (GdkTexture *texture)
{
g_return_val_if_fail (GDK_IS_TEXTURE (texture), FALSE);
return GDK_TEXTURE_GET_CLASS (texture)->is_hdr (texture);
}

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_6
GdkColorProfile * gdk_texture_get_color_profile (GdkTexture *texture) G_GNUC_PURE;
GDK_AVAILABLE_IN_ALL
void gdk_texture_download (GdkTexture *texture,

View File

@@ -15,6 +15,7 @@ struct _GdkTexture
int width;
int height;
GdkColorProfile *color_profile;
gpointer render_key;
gpointer render_data;
@@ -33,12 +34,16 @@ struct _GdkTextureClass {
void (* download_float) (GdkTexture *texture,
float *data,
gsize stride);
gboolean (* is_hdr) (GdkTexture *texture);
};
gboolean gdk_texture_can_load (GBytes *bytes);
gboolean gdk_texture_is_hdr (GdkTexture *self);
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 *color_profile);
/* NB: GdkMemoryTexture */
GdkTexture * gdk_texture_download_texture (GdkTexture *texture);

View File

@@ -73,6 +73,9 @@ typedef cairo_rectangle_int_t GdkRectangle;
/* Forward declarations of commonly used types */
typedef struct _GdkRGBA GdkRGBA;
typedef struct _GdkColorProfile GdkColorProfile;
typedef struct _GdkICCProfile GdkICCProfile;
typedef struct _GdkDerivedProfile GdkDerivedProfile;
typedef struct _GdkContentFormats GdkContentFormats;
typedef struct _GdkContentProvider GdkContentProvider;
typedef struct _GdkCursor GdkCursor;

View File

@@ -19,9 +19,10 @@
#include "gdkjpegprivate.h"
#include "gdkiccprofile.h"
#include "gdkintl.h"
#include "gdktexture.h"
#include "gdkmemorytextureprivate.h"
#include "gdktexture.h"
#include "gdkprofilerprivate.h"
@@ -173,9 +174,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);
@@ -195,6 +199,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);
@@ -252,14 +259,29 @@ 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 (gdk_icc_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);
@@ -279,9 +301,12 @@ gdk_save_jpeg (GdkTexture *texture)
guchar *input = NULL;
guchar *row;
int width, height, stride;
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;
@@ -315,6 +340,12 @@ gdk_save_jpeg (GdkTexture *texture)
jpeg_start_compress (&info, TRUE);
bytes = gdk_icc_profile_get_icc_profile (GDK_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 = &input[info.next_scanline * stride];

View File

@@ -19,12 +19,15 @@
#include "gdkpngprivate.h"
#include "gdkiccprofileprivate.h"
#include "gdkintl.h"
#include "gdkmemoryformatprivate.h"
#include "gdkmemorytextureprivate.h"
#include "gdkprofilerprivate.h"
#include "gdktexture.h"
#include "gdktextureprivate.h"
#include "gsk/ngl/fp16private.h"
#include <png.h>
#include <stdio.h>
@@ -126,169 +129,101 @@ png_simple_warning_callback (png_structp png,
{
}
/* }}} */
/* {{{ Format conversion */
static void
unpremultiply (guchar *data,
int width,
int height)
static GdkColorProfile *
gdk_png_get_color_profile (png_struct *png,
png_info *info)
{
gsize x, y;
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;
for (y = 0; y < height; y++)
if (png_get_iCCP (png, info, &name, NULL, &icc_data, &icc_len))
{
for (x = 0; x < width; x++)
{
guchar *b = &data[x * 4];
guint32 pixel;
guchar alpha;
GBytes *bytes = g_bytes_new (icc_data, icc_len);
memcpy (&pixel, b, sizeof (guint32));
alpha = (pixel & 0xff000000) >> 24;
if (alpha == 0)
{
b[0] = 0;
b[1] = 0;
b[2] = 0;
b[3] = 0;
}
else
{
b[0] = (((pixel & 0x00ff0000) >> 16) * 255 + alpha / 2) / alpha;
b[1] = (((pixel & 0x0000ff00) >> 8) * 255 + alpha / 2) / alpha;
b[2] = (((pixel & 0x000000ff) >> 0) * 255 + alpha / 2) / alpha;
b[3] = alpha;
}
}
data += width * 4;
profile = GDK_COLOR_PROFILE (gdk_icc_profile_new_from_icc_bytes (bytes, NULL));
g_bytes_unref (bytes);
if (profile)
return profile;
}
}
static void
unpremultiply_float_to_16bit (guchar *data,
int width,
int height)
{
gsize x, y;
float *src = (float *)data;;
guint16 *dest = (guint16 *)data;
if (png_get_sRGB (png, info, &intent))
return g_object_ref (gdk_color_profile_get_srgb ());
for (y = 0; y < height; y++)
/* 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))
{
for (x = 0; x < width; x++)
{
float r, g, b, a;
if (gamma == 2.4)
return g_object_ref (gdk_color_profile_get_srgb ());
r = src[0];
g = src[1];
b = src[2];
a = src[3];
if (a == 0)
{
dest[0] = 0;
dest[1] = 0;
dest[2] = 0;
dest[3] = 0;
}
else
{
dest[0] = (guint16) CLAMP (65536.f * r / a, 0.f, 65535.f);
dest[1] = (guint16) CLAMP (65536.f * g / a, 0.f, 65535.f);
dest[2] = (guint16) CLAMP (65536.f * b / a, 0.f, 65535.f);
dest[3] = (guint16) CLAMP (65536.f * a, 0.f, 65535.f);
}
dest += 4;
src += 4;
}
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 }
};
}
}
static inline int
multiply_alpha (int alpha, int color)
{
int temp = (alpha * color) + 0x80;
return ((temp + (temp >> 8)) >> 8);
}
static void
premultiply_data (png_structp png,
png_row_infop row_info,
png_bytep data)
{
unsigned int i;
for (i = 0; i < row_info->rowbytes; i += 4)
else
{
uint8_t *base = &data[i];
uint8_t alpha = base[3];
uint32_t p;
if (alpha == 0)
{
p = 0;
}
else
{
uint8_t red = base[0];
uint8_t green = base[1];
uint8_t blue = base[2];
if (alpha != 0xff)
{
red = multiply_alpha (alpha, red);
green = multiply_alpha (alpha, green);
blue = multiply_alpha (alpha, blue);
}
p = ((uint32_t)alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
}
memcpy (base, &p, sizeof (uint32_t));
}
}
static void
convert_bytes_to_data (png_structp png,
png_row_infop row_info,
png_bytep data)
{
unsigned int i;
for (i = 0; i < row_info->rowbytes; i += 4)
{
uint8_t *base = &data[i];
uint8_t red = base[0];
uint8_t green = base[1];
uint8_t blue = base[2];
uint32_t pixel;
pixel = (0xffu << 24) | (red << 16) | (green << 8) | (blue << 0);
memcpy (base, &pixel, sizeof (uint32_t));
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 (gdk_icc_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
premultiply_16bit (guchar *data,
int width,
int height,
int stride)
gdk_png_set_icc_profile (png_struct *png,
png_info *info,
GdkICCProfile *profile)
{
gsize x, y;
guint16 *src;
for (y = 0; y < height; y++)
/* FIXME: allow deconstructing RGB profiles into gAMA and cHRM instead of
* falling back to iCCP
*/
if (profile == gdk_color_profile_get_srgb ())
{
src = (guint16 *)data;
for (x = 0; x < width; x++)
{
float alpha = src[x * 4 + 3] / 65535.f;
src[x * 4 ] = (guint16) CLAMP (src[x * 4 ] * alpha, 0.f, 65535.f);
src[x * 4 + 1] = (guint16) CLAMP (src[x * 4 + 1] * alpha, 0.f, 65535.f);
src[x * 4 + 2] = (guint16) CLAMP (src[x * 4 + 2] * alpha, 0.f, 65535.f);
}
data += stride;
png_set_sRGB_gAMA_and_cHRM (png, info, /* FIXME */ PNG_sRGB_INTENT_PERCEPTUAL);
}
else if (GDK_IS_ICC_PROFILE (profile))
{
GBytes *bytes = gdk_icc_profile_get_icc_profile (profile);
png_set_iCCP (png, info,
"ICC profile",
0,
g_bytes_get_data (bytes, NULL),
g_bytes_get_size (bytes));
}
else
g_assert_not_reached ();
}
/* }}} */
@@ -308,6 +243,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;
@@ -354,9 +290,6 @@ gdk_load_png (GBytes *bytes,
if (png_get_valid (png, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha (png);
if (depth == 8)
png_set_filler (png, 0xff, PNG_FILLER_AFTER);
if (depth < 8)
png_set_packing (png);
@@ -367,18 +300,20 @@ gdk_load_png (GBytes *bytes,
if (interlace != PNG_INTERLACE_NONE)
png_set_interlace_handling (png);
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
png_set_swap (png);
#endif
png_read_update_info (png, info);
png_get_IHDR (png, info,
&width, &height, &depth,
&color_type, &interlace, NULL, NULL);
if ((depth != 8 && depth != 16) ||
!(color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_RGB_ALPHA))
if (depth != 8 && depth != 16)
{
png_destroy_read_struct (&png, &info, NULL);
g_set_error (error,
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT,
_("Failed to parse png image"));
_("Unsupported depth %u in png image"), depth);
return NULL;
}
@@ -387,35 +322,41 @@ gdk_load_png (GBytes *bytes,
case PNG_COLOR_TYPE_RGB_ALPHA:
if (depth == 8)
{
format = GDK_MEMORY_DEFAULT;
png_set_read_user_transform_fn (png, premultiply_data);
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
format = GDK_MEMORY_R8G8B8A8;
#elif G_BYTE_ORDER == G_BIG_ENDIAN
format = GDK_MEMORY_A8B8G8R8;
#endif
}
else
{
format = GDK_MEMORY_R16G16B16A16_PREMULTIPLIED;
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
png_set_swap (png);
#endif
format = GDK_MEMORY_R16G16B16A16;
}
break;
case PNG_COLOR_TYPE_RGB:
if (depth == 8)
{
format = GDK_MEMORY_DEFAULT;
png_set_read_user_transform_fn (png, convert_bytes_to_data);
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
format = GDK_MEMORY_R8G8B8;
#elif G_BYTE_ORDER == G_BIG_ENDIAN
format = GDK_MEMORY_B8G8R8;
#endif
}
else
else if (depth == 16)
{
format = GDK_MEMORY_R16G16B16;
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
png_set_swap (png);
#endif
}
break;
default:
g_assert_not_reached ();
png_destroy_read_struct (&png, &info, NULL);
g_set_error (error,
GDK_TEXTURE_ERROR, GDK_TEXTURE_ERROR_UNSUPPORTED_CONTENT,
_("Unsupportd color type %u in png image"), color_type);
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)
@@ -426,6 +367,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);
@@ -441,12 +383,13 @@ gdk_load_png (GBytes *bytes,
png_read_image (png, row_pointers);
png_read_end (png, info);
if (format == GDK_MEMORY_R16G16B16A16_PREMULTIPLIED)
premultiply_16bit (buffer, width, height, stride);
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);
@@ -467,20 +410,22 @@ gdk_save_png (GdkTexture *texture)
png_struct *png = NULL;
png_info *info;
png_io io = { NULL, 0, 0 };
guint width, height, stride;
guchar *data = NULL;
guchar *row;
int width, height;
gsize stride;
const guchar *data;
int y;
GdkTexture *mtexture;
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_ICC_PROFILE (gdk_texture_get_color_profile (texture));
mtexture = gdk_texture_download_texture (texture);
format = gdk_memory_texture_get_format (GDK_MEMORY_TEXTURE (mtexture));
memtex = GDK_MEMORY_TEXTURE (gdk_texture_download_texture (texture));
format = gdk_memory_texture_get_format (memtex);
switch (format)
{
@@ -491,29 +436,42 @@ gdk_save_png (GdkTexture *texture)
case GDK_MEMORY_A8R8G8B8:
case GDK_MEMORY_R8G8B8A8:
case GDK_MEMORY_A8B8G8R8:
case GDK_MEMORY_R8G8B8:
case GDK_MEMORY_B8G8R8:
stride = width * 4;
data = g_malloc_n (stride, height);
gdk_texture_download (mtexture, data, stride);
unpremultiply (data, width, height);
#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_R16G16B16:
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
case GDK_MEMORY_R16G16B16_FLOAT:
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
case GDK_MEMORY_R32G32B32_FLOAT:
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
data = g_malloc_n (width * 16, height);
gdk_texture_download_float (mtexture, (float *)data, width * 4);
unpremultiply_float_to_16bit (data, width, height);
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;
case GDK_MEMORY_R16G16B16A16:
case GDK_MEMORY_R16G16B16A16_PREMULTIPLIED:
case GDK_MEMORY_R16G16B16A16_FLOAT:
case GDK_MEMORY_R16G16B16A16_FLOAT_PREMULTIPLIED:
case GDK_MEMORY_R32G32B32A32_FLOAT:
case GDK_MEMORY_R32G32B32A32_FLOAT_PREMULTIPLIED:
format = GDK_MEMORY_R16G16B16A16;
png_format = PNG_COLOR_TYPE_RGB_ALPHA;
stride = width * 8;
depth = 16;
break;
case GDK_MEMORY_R16G16B16:
case GDK_MEMORY_R16G16B16_FLOAT:
case GDK_MEMORY_R32G32B32_FLOAT:
format = GDK_MEMORY_R16G16B16;
png_format = PNG_COLOR_TYPE_RGB;
depth = 16;
break;
@@ -540,12 +498,14 @@ gdk_save_png (GdkTexture *texture)
if (sigsetjmp (png_jmpbuf (png), 1))
{
g_free (data);
g_object_unref (memtex);
g_free (io.data);
png_destroy_read_struct (&png, &info, NULL);
return NULL;
}
memtex = gdk_memory_texture_convert (memtex, format, color_profile, NULL);
png_set_write_fn (png, &io, png_write_func, png_flush_func);
png_set_IHDR (png, info, width, height, depth,
@@ -554,20 +514,24 @@ gdk_save_png (GdkTexture *texture)
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT);
gdk_png_set_icc_profile (png, info, GDK_ICC_PROFILE (color_profile));
png_write_info (png, info);
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
png_set_swap (png);
#endif
for (y = 0, row = data; y < height; y++, row += stride)
png_write_rows (png, &row, 1);
data = gdk_memory_texture_get_data (memtex);
stride = gdk_memory_texture_get_stride (memtex);
for (y = 0; y < height; y++)
png_write_row (png, data + y * stride);
png_write_end (png, info);
png_destroy_write_struct (&png, &info);
g_free (data);
g_object_unref (memtex);
return g_bytes_new_take (io.data, io.size);
}

View File

@@ -19,7 +19,9 @@
#include "gdktiffprivate.h"
#include "gdkiccprofileprivate.h"
#include "gdkintl.h"
#include "gdkmemoryformatprivate.h"
#include "gdkmemorytextureprivate.h"
#include "gdkprofilerprivate.h"
#include "gdktexture.h"
@@ -243,6 +245,44 @@ flip_02 (guchar *data,
}
}
/* }}} */
/* {{{ Color profile handling */
static GdkColorProfile *
gdk_tiff_get_icc_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 (gdk_icc_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_icc_profile (TIFF *tiff,
GdkICCProfile *profile)
{
GBytes *bytes = gdk_icc_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 */
@@ -276,11 +316,13 @@ gdk_save_tiff (GdkTexture *texture)
GBytes *result = NULL;
GdkTexture *memory_texture;
GdkMemoryFormat format;
GdkICCProfile *icc_profile;
tif = tiff_open_write (&result);
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
icc_profile = GDK_ICC_PROFILE (gdk_texture_get_color_profile (texture));
memory_texture = gdk_texture_download_texture (texture);
format = gdk_memory_texture_get_format (GDK_MEMORY_TEXTURE (memory_texture));
@@ -323,7 +365,8 @@ gdk_save_tiff (GdkTexture *texture)
TIFFSetField (tif, TIFFTAG_SAMPLEFORMAT, sample_format);
TIFFSetField (tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField (tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
// TODO: save gamma / colorspace
gdk_tiff_set_icc_profile (tif, icc_profile);
if (samples_per_pixel > 3)
{
@@ -360,12 +403,13 @@ gdk_save_tiff (GdkTexture *texture)
}
static GdkTexture *
load_fallback (TIFF *tif,
GError **error)
load_fallback (TIFF *tif,
GError **error)
{
int width, height;
guchar *data;
GBytes *bytes;
GdkColorProfile *profile;
GdkTexture *texture;
TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &width);
@@ -382,14 +426,18 @@ load_fallback (TIFF *tif,
return NULL;
}
profile = gdk_tiff_get_icc_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);
g_object_unref (profile);
return texture;
}
@@ -411,6 +459,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;
@@ -497,14 +546,18 @@ gdk_load_tiff (GBytes *input_bytes,
line += stride;
}
profile = gdk_tiff_get_icc_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);
g_object_unref (profile);
TIFFClose (tif);
if (GDK_PROFILER_IS_RUNNING)

View File

@@ -4,12 +4,15 @@ gdk_public_sources = files([
'gdkcairo.c',
'gdkcairocontext.c',
'gdkclipboard.c',
'gdkcolor.c',
'gdkcolorprofile.c',
'gdkcontentdeserializer.c',
'gdkcontentformats.c',
'gdkcontentprovider.c',
'gdkcontentproviderimpl.c',
'gdkcontentserializer.c',
'gdkcursor.c',
'gdkderivedprofile.c',
'gdkdevice.c',
'gdkdevicepad.c',
'gdkdevicetool.c',
@@ -28,8 +31,10 @@ gdk_public_sources = files([
'gdkglobals.c',
'gdkgltexture.c',
'gdkhsla.c',
'gdkiccprofile.c',
'gdkkeys.c',
'gdkkeyuni.c',
'gdkmemoryformat.c',
'gdkmemorytexture.c',
'gdkmonitor.c',
'gdkpaintable.c',
@@ -63,12 +68,14 @@ gdk_public_headers = files([
'gdkcairo.h',
'gdkcairocontext.h',
'gdkclipboard.h',
'gdkcolorprofile.h',
'gdkcontentdeserializer.h',
'gdkcontentformats.h',
'gdkcontentprovider.h',
'gdkcontentproviderimpl.h',
'gdkcontentserializer.h',
'gdkcursor.h',
'gdkderivedprofile.h',
'gdkdevice.h',
'gdkdevicepad.h',
'gdkdevicetool.h',
@@ -82,6 +89,7 @@ gdk_public_headers = files([
'gdkframetimings.h',
'gdkglcontext.h',
'gdkgltexture.h',
'gdkiccprofile.h',
'gdkkeys.h',
'gdkkeysyms.h',
'gdkmemorytexture.h',
@@ -203,6 +211,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

@@ -38,6 +38,7 @@
#include "gdkkeysprivate.h"
#include "gdkmarshalers.h"
#include "xsettings-client.h"
#include "gdkiccprofileprivate.h"
#include "gdkcairocontext-x11.h"
#include "gdkclipboard-x11.h"
@@ -1403,6 +1404,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 (gdk_icc_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 +1536,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 +1984,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 ())
{
@@ -3175,7 +3177,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

@@ -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
@@ -77,8 +81,8 @@ gsk_cairo_renderer_do_render (GskRenderer *renderer,
cairo_t *cr,
GskRenderNode *root)
{
#ifdef G_ENABLE_DEBUG
GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
#ifdef G_ENABLE_DEBUG
GskProfiler *profiler;
gint64 cpu_time;
#endif
@@ -103,11 +107,15 @@ 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;
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, ceil (viewport->size.width), ceil (viewport->size.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);
@@ -152,7 +160,65 @@ 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;
GdkMemoryTexture *sub;
cairo_region_get_rectangle (frame_region, i, &rect);
rect.x -= extents.x;
rect.y -= extents.y;
sub = gdk_memory_texture_convert (g_object_ref (GDK_MEMORY_TEXTURE (color_correct)),
GDK_MEMORY_DEFAULT,
target_profile,
&rect);
cairo_surface = gdk_texture_download_surface (GDK_TEXTURE (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);
@@ -173,6 +239,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

@@ -725,3 +725,8 @@ gsk_value_dup_render_node (const GValue *value)
return gsk_render_node_ref (value->data[0].v_pointer);
}
gboolean
gsk_render_node_is_hdr (const GskRenderNode *node)
{
return node->is_hdr;
}

View File

@@ -27,11 +27,20 @@
#include "gskroundedrectprivate.h"
#include "gsktransformprivate.h"
#include "gdk/gdkcolorprivate.h"
#include "gdk/gdktextureprivate.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
static inline void
gsk_cairo_rectangle (cairo_t *cr,
const graphene_rect_t *rect)
@@ -179,6 +188,7 @@ gsk_linear_gradient_node_draw (GskRenderNode *node,
cairo_t *cr)
{
GskLinearGradientNode *self = (GskLinearGradientNode *) node;
GdkColorProfile *profile;
cairo_pattern_t *pattern;
gsize i;
@@ -188,14 +198,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);
@@ -812,11 +828,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
@@ -845,9 +867,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;
@@ -860,46 +882,26 @@ 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 *color_profile;
cairo_pattern_t *pattern;
graphene_point_t corner;
float radius;
gsize i;
color_profile = gdk_cairo_get_color_profile (cr);
pattern = cairo_pattern_create_mesh ();
graphene_rect_get_top_right (&node->bounds, &corner);
@@ -915,26 +917,31 @@ gsk_conic_gradient_node_draw (GskRenderNode *node,
{
GskColorStop *stop1 = &self->stops[MAX (i, 1) - 1];
GskColorStop *stop2 = &self->stops[MIN (i, self->n_stops - 1)];
GdkColor stop1_color, stop2_color;
double offset1 = i > 0 ? stop1->offset : 0;
double offset2 = i < self->n_stops ? stop2->offset : 1;
double start_angle, end_angle;
offset1 = offset1 * 360 + self->rotation - 90;
offset2 = offset2 * 360 + self->rotation - 90;
gdk_color_convert_rgba (&stop1_color, color_profile, &stop1->color);
gdk_color_convert_rgba (&stop2_color, 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,
color_profile,
&stop1_color,
&stop2_color,
(start_angle - offset1) / (offset2 - offset1));
gdk_color_mix (&end_color,
color_profile,
&stop1_color,
&stop2_color,
(end_angle - offset1) / (offset2 - offset1));
gsk_conic_gradient_node_add_patch (pattern,
radius,
@@ -943,6 +950,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);
@@ -1158,7 +1168,8 @@ struct _GskBorderNode
static void
gsk_border_node_mesh_add_patch (cairo_pattern_t *pattern,
const GdkRGBA *color,
GdkColorProfile *color_profile,
const GdkRGBA *rgba,
double x0,
double y0,
double x1,
@@ -1168,16 +1179,26 @@ gsk_border_node_mesh_add_patch (cairo_pattern_t *pattern,
double x3,
double y3)
{
GdkColor color;
const float *components;
float alpha;
gdk_color_convert_rgba (&color, color_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);
gdk_color_finish (&color);
}
static void
@@ -1222,11 +1243,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);
@@ -1244,6 +1267,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,
@@ -1255,6 +1279,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,
@@ -1266,6 +1291,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,
@@ -1277,6 +1303,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,
@@ -1483,7 +1510,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);
@@ -1560,6 +1588,8 @@ gsk_texture_node_new (GdkTexture *texture,
self->texture = g_object_ref (texture);
graphene_rect_init_from_rect (&node->bounds, bounds);
node->is_hdr = gdk_texture_is_hdr (texture);
return node;
}
@@ -2730,11 +2760,13 @@ gsk_container_node_new (GskRenderNode **children,
self->children[0] = gsk_render_node_ref (children[0]);
graphene_rect_init_from_rect (&bounds, &(children[0]->bounds));
node->is_hdr = gsk_render_node_is_hdr (children[0]);
for (guint i = 1; i < n_children; i++)
{
self->children[i] = gsk_render_node_ref (children[i]);
graphene_rect_union (&bounds, &(children[i]->bounds), &bounds);
node->is_hdr |= gsk_render_node_is_hdr (children[i]);
}
graphene_rect_init_from_rect (&node->bounds, &bounds);
@@ -2965,6 +2997,8 @@ gsk_transform_node_new (GskRenderNode *child,
&child->bounds,
&node->bounds);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -3100,6 +3134,8 @@ gsk_opacity_node_new (GskRenderNode *child,
graphene_rect_init_from_rect (&node->bounds, &child->bounds);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -3302,6 +3338,8 @@ gsk_color_matrix_node_new (GskRenderNode *child,
graphene_rect_init_from_rect (&node->bounds, &child->bounds);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -3451,6 +3489,8 @@ gsk_repeat_node_new (const graphene_rect_t *bounds,
else
graphene_rect_init_from_rect (&self->child_bounds, &child->bounds);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -3582,6 +3622,8 @@ gsk_clip_node_new (GskRenderNode *child,
graphene_rect_intersection (&self->clip, &child->bounds, &node->bounds);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -3713,6 +3755,8 @@ gsk_rounded_clip_node_new (GskRenderNode *child,
graphene_rect_intersection (&self->clip.bounds, &child->bounds, &node->bounds);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -3932,6 +3976,8 @@ gsk_shadow_node_new (GskRenderNode *child,
gsk_shadow_node_get_bounds (self, &node->bounds);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -4125,6 +4171,8 @@ gsk_blend_node_new (GskRenderNode *bottom,
graphene_rect_union (&bottom->bounds, &top->bounds, &node->bounds);
node->is_hdr = gsk_render_node_is_hdr (bottom) || gsk_render_node_is_hdr (top);
return node;
}
@@ -4273,6 +4321,8 @@ gsk_cross_fade_node_new (GskRenderNode *start,
graphene_rect_union (&start->bounds, &end->bounds, &node->bounds);
node->is_hdr = gsk_render_node_is_hdr (start) || gsk_render_node_is_hdr (end);
return node;
}
@@ -4357,12 +4407,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;
@@ -4375,8 +4435,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,
@@ -4864,6 +4930,8 @@ gsk_blur_node_new (GskRenderNode *child,
- clip_radius,
- clip_radius);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -4986,6 +5054,8 @@ gsk_debug_node_new (GskRenderNode *child,
graphene_rect_init_from_rect (&node->bounds, &child->bounds);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -5150,7 +5220,10 @@ gsk_gl_shader_node_new (GskGLShader *shader,
{
self->children = g_malloc_n (n_children, sizeof (GskRenderNode *));
for (guint i = 0; i < n_children; i++)
self->children[i] = gsk_render_node_ref (children[i]);
{
self->children[i] = gsk_render_node_ref (children[i]);
node->is_hdr |= gsk_render_node_is_hdr (children[i]);
}
}
return node;

View File

@@ -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);

View File

@@ -27,6 +27,8 @@ struct _GskRenderNode
gatomicrefcount ref_count;
graphene_rect_t bounds;
guint is_hdr : 1;
};
struct _GskRenderNodeClass
@@ -109,6 +111,7 @@ GskRenderNode ** gsk_container_node_get_children (const GskRenderNode *no
void gsk_transform_node_get_translate (const GskRenderNode *node,
float *dx,
float *dy);
gboolean gsk_render_node_is_hdr (const GskRenderNode *node);
G_END_DECLS

View File

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

View File

@@ -26,7 +26,9 @@
#include <string.h>
#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>
@@ -528,7 +530,7 @@ gsk_ngl_command_queue_begin_draw (GskNglCommandQueue *self,
GskNglCommandBatch *batch;
g_assert (GSK_IS_NGL_COMMAND_QUEUE (self));
g_assert (self->in_draw == FALSE);
g_assert (!self->in_draw);
g_assert (width <= G_MAXUINT16);
g_assert (height <= G_MAXUINT16);
@@ -566,6 +568,7 @@ gsk_ngl_command_queue_end_draw (GskNglCommandQueue *self)
GskNglCommandBatch *batch;
g_assert (GSK_IS_NGL_COMMAND_QUEUE (self));
g_assert (self->in_draw);
g_assert (self->batches.len > 0);
if (will_ignore_batch (self))
@@ -1231,6 +1234,7 @@ gboolean
gsk_ngl_command_queue_create_render_target (GskNglCommandQueue *self,
int width,
int height,
int format,
int min_filter,
int mag_filter,
guint *out_fbo_id,
@@ -1247,6 +1251,7 @@ gsk_ngl_command_queue_create_render_target (GskNglCommandQueue *self,
texture_id = gsk_ngl_command_queue_create_texture (self,
width, height,
format,
min_filter, mag_filter);
if (texture_id == -1)
@@ -1272,6 +1277,7 @@ int
gsk_ngl_command_queue_create_texture (GskNglCommandQueue *self,
int width,
int height,
int format,
int min_filter,
int mag_filter)
{
@@ -1296,9 +1302,9 @@ gsk_ngl_command_queue_create_texture (GskNglCommandQueue *self,
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (gdk_gl_context_get_use_es (self->context))
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexImage2D (GL_TEXTURE_2D, 0, format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
else
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
glTexImage2D (GL_TEXTURE_2D, 0, format, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
/* Restore the previous texture if it was set */
if (self->attachments->textures[0].id != 0)
@@ -1319,6 +1325,109 @@ gsk_ngl_command_queue_create_framebuffer (GskNglCommandQueue *self)
return fbo_id;
}
static void
gsk_ngl_command_queue_do_upload_texture (GdkGLContext *context,
GdkTexture *texture,
int x,
int y,
int width,
int height,
guint texture_target,
GskConversion *conversion)
{
GdkMemoryTexture *memory_texture;
GdkMemoryFormat data_format;
GdkColorProfile *data_profile;
GLenum gl_internalformat;
GLenum gl_format;
GLenum gl_type;
gsize bpp, stride;
const guchar *data;
gboolean convert_locally = FALSE;
g_return_if_fail (GDK_IS_GL_CONTEXT (context));
memory_texture = GDK_MEMORY_TEXTURE (gdk_texture_download_texture (texture));
data_format = gdk_memory_texture_get_format (memory_texture);
data_profile = gdk_texture_get_color_profile (GDK_TEXTURE (memory_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
convert_locally = TRUE;
if (!gdk_memory_format_gl_format (data_format,
gdk_gl_context_get_use_es (context),
&gl_internalformat,
&gl_format,
&gl_type))
{
if (!gdk_gl_context_get_use_es (context))
{
*conversion |= GSK_CONVERSION_PREMULTIPLY;
}
else
{
convert_locally = TRUE;
data_format = GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
if (!gdk_memory_format_gl_format (GDK_MEMORY_R8G8B8A8_PREMULTIPLIED,
gdk_gl_context_get_use_es (context),
&gl_internalformat,
&gl_format,
&gl_type))
{
g_assert_not_reached ();
}
}
}
if (convert_locally)
{
memory_texture = gdk_memory_texture_convert (memory_texture,
data_format,
gdk_color_profile_get_srgb_linear (),
&(GdkRectangle) { x, y, width, height });
*conversion = 0;
}
bpp = gdk_memory_format_bytes_per_pixel (data_format);
stride = gdk_memory_texture_get_stride (memory_texture);
data = gdk_memory_texture_get_data (memory_texture);
/* 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 == width * bpp)
{
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
glPixelStorei (GL_UNPACK_ALIGNMENT, 4);
}
else if ((stride % bpp == 0) &&
(!gdk_gl_context_get_use_es (context) ||
gdk_gl_context_has_version (context, 3, 0) ||
gdk_gl_context_has_unpack_subimage (context)))
{
glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp);
glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data);
glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
}
else
{
int i;
glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, NULL);
for (i = 0; i < height; i++)
glTexSubImage2D (texture_target, 0, 0, i, width, 1, gl_format, gl_type, data + (i * stride));
}
g_clear_object (&memory_texture);
}
int
gsk_ngl_command_queue_upload_texture (GskNglCommandQueue *self,
GdkTexture *texture,
@@ -1327,14 +1436,11 @@ gsk_ngl_command_queue_upload_texture (GskNglCommandQueue *self,
guint width,
guint height,
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;
GdkMemoryFormat data_format;
const guchar *data;
gsize data_stride;
gsize bpp;
int format;
int texture_id;
g_assert (GSK_IS_NGL_COMMAND_QUEUE (self));
@@ -1344,6 +1450,8 @@ gsk_ngl_command_queue_upload_texture (GskNglCommandQueue *self,
g_assert (min_filter == GL_LINEAR || min_filter == GL_NEAREST);
g_assert (mag_filter == GL_LINEAR || min_filter == GL_NEAREST);
*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. "
@@ -1353,47 +1461,33 @@ gsk_ngl_command_queue_upload_texture (GskNglCommandQueue *self,
height = MAX (height, self->max_texture_size);
}
texture_id = gsk_ngl_command_queue_create_texture (self, width, height, min_filter, mag_filter);
format = gdk_texture_is_hdr (texture) ? GL_RGBA16F : GL_RGBA8;
texture_id = gsk_ngl_command_queue_create_texture (self,
width, height,
format,
min_filter, mag_filter);
if (texture_id == -1)
return texture_id;
if (GDK_IS_MEMORY_TEXTURE (texture))
{
GdkMemoryTexture *memory_texture = GDK_MEMORY_TEXTURE (texture);
data = gdk_memory_texture_get_data (memory_texture);
data_format = gdk_memory_texture_get_format (memory_texture);
data_stride = gdk_memory_texture_get_stride (memory_texture);
}
else
{
/* Fall back to downloading to a surface */
surface = gdk_texture_download_surface (texture);
cairo_surface_flush (surface);
data = cairo_image_surface_get_data (surface);
data_format = GDK_MEMORY_DEFAULT;
data_stride = cairo_image_surface_get_stride (surface);
}
self->n_uploads++;
bpp = gdk_memory_format_bytes_per_pixel (data_format);
/* Switch to texture0 as 2D. We'll restore it later. */
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, texture_id);
gdk_gl_context_upload_texture (gdk_gl_context_get_current (),
data + x_offset * bpp + y_offset * data_stride,
width, height, data_stride,
data_format, GL_TEXTURE_2D);
gsk_ngl_command_queue_do_upload_texture (self->context,
texture,
x_offset, y_offset,
width, height,
GL_TEXTURE_2D,
conversion);
/* Restore previous texture state if any */
if (self->attachments->textures[0].id > 0)
glBindTexture (self->attachments->textures[0].target,
self->attachments->textures[0].id);
g_clear_pointer (&surface, cairo_surface_destroy);
if (gdk_profiler_is_running ())
gdk_profiler_add_markf (start_time, GDK_PROFILER_CURRENT_TIME-start_time,
"Upload Texture",

View File

@@ -279,6 +279,13 @@ void gsk_ngl_command_queue_execute (GskNglCommandQue
guint surface_height,
guint scale_factor,
const cairo_region_t *scissor);
typedef enum {
GSK_CONVERSION_LINEARIZE = 1 << 0,
GSK_CONVERSION_PREMULTIPLY = 1 << 1,
GSK_CONVERSION_FLIP = 1 << 2,
} GskConversion;
int gsk_ngl_command_queue_upload_texture (GskNglCommandQueue *self,
GdkTexture *texture,
guint x_offset,
@@ -286,16 +293,19 @@ int gsk_ngl_command_queue_upload_texture (GskNglCommandQue
guint width,
guint height,
int min_filter,
int mag_filter);
int mag_filter,
GskConversion *remaining);
int gsk_ngl_command_queue_create_texture (GskNglCommandQueue *self,
int width,
int height,
int format,
int min_filter,
int mag_filter);
guint gsk_ngl_command_queue_create_framebuffer (GskNglCommandQueue *self);
gboolean gsk_ngl_command_queue_create_render_target (GskNglCommandQueue *self,
int width,
int height,
int format,
int min_filter,
int mag_filter,
guint *out_fbo_id,

View File

@@ -27,6 +27,7 @@
#include <gdk/gdkdisplayprivate.h>
#include <gdk/gdktextureprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdkcolorprofileprivate.h>
#include <gsk/gskdebugprivate.h>
#include <gsk/gskglshaderprivate.h>
#include <gsk/gskrendererprivate.h>
@@ -183,6 +184,7 @@ gsk_ngl_driver_create_atlas (GskNglDriver *self)
atlas->texture_id = gsk_ngl_command_queue_create_texture (self->command_queue,
atlas->width,
atlas->height,
GL_RGBA8,
GL_LINEAR,
GL_LINEAR);
@@ -715,6 +717,137 @@ gsk_ngl_driver_cache_texture (GskNglDriver *self,
g_hash_table_insert (self->texture_id_to_key, GUINT_TO_POINTER (texture_id), k);
}
static void
draw_rect (GskNglCommandQueue *command_queue,
float min_x,
float min_y,
float max_x,
float max_y,
gboolean flip)
{
GskNglDrawVertex *vertices = gsk_ngl_command_queue_add_vertices (command_queue);
float min_u = 0;
float max_u = 1;
float min_v = flip ? 0 : 1;
float max_v = flip ? 1 : 0;
guint16 c[4] = { FP16_ZERO, FP16_ZERO, FP16_ZERO, FP16_ZERO };
vertices[0] = (GskNglDrawVertex) { .position = { min_x, min_y }, .uv = { min_u, min_v }, .color = { c[0], c[1], c[2], c[3] } };
vertices[1] = (GskNglDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c[0], c[1], c[2], c[3] } };
vertices[2] = (GskNglDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c[0], c[1], c[2], c[3] } };
vertices[3] = (GskNglDrawVertex) { .position = { max_x, max_y }, .uv = { max_u, max_v }, .color = { c[0], c[1], c[2], c[3] } };
vertices[4] = (GskNglDrawVertex) { .position = { min_x, max_y }, .uv = { min_u, max_v }, .color = { c[0], c[1], c[2], c[3] } };
vertices[5] = (GskNglDrawVertex) { .position = { max_x, min_y }, .uv = { max_u, min_v }, .color = { c[0], c[1], c[2], c[3] } };
}
static void
set_viewport_for_size (GskNglDriver *self,
GskNglProgram *program,
float width,
float height)
{
float viewport[4] = { 0, 0, width, height };
gsk_ngl_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 (GskNglDriver *self,
GskNglProgram *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_ngl_uniform_state_set_matrix (program->uniforms,
program->program_info,
UNIFORM_SHARED_PROJECTION, 0,
&projection);
self->stamps[UNIFORM_SHARED_PROJECTION]++;
}
static void
reset_modelview (GskNglDriver *self,
GskNglProgram *program)
{
graphene_matrix_t modelview;
graphene_matrix_init_identity (&modelview);
gsk_ngl_uniform_state_set_matrix (program->uniforms,
program->program_info,
UNIFORM_SHARED_MODELVIEW, 0,
&modelview);
self->stamps[UNIFORM_SHARED_MODELVIEW]++;
}
static GskNglTexture *
gsk_ngl_driver_convert_texture (GskNglDriver *self,
int texture_id,
int width,
int height,
int format,
int min_filter,
int mag_filter,
GskConversion conversion)
{
GskNglRenderTarget *target;
int prev_fbo;
GskNglProgram *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_ngl_driver_create_render_target (self,
width, height,
format,
min_filter, mag_filter,
&target);
prev_fbo = gsk_ngl_command_queue_bind_framebuffer (self->command_queue, target->framebuffer_id);
gsk_ngl_command_queue_clear (self->command_queue, 0, &GRAPHENE_RECT_INIT (0, 0, width, height));
gsk_ngl_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_ngl_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_ngl_command_queue_end_draw (self->command_queue);
gsk_ngl_command_queue_bind_framebuffer (self->command_queue, prev_fbo);
texture_id = gsk_ngl_driver_release_render_target (self, target, FALSE);
return g_hash_table_lookup (self->textures, GUINT_TO_POINTER (texture_id));
}
/**
* gsk_ngl_driver_load_texture:
* @self: a `GdkTexture`
@@ -745,17 +878,28 @@ gsk_ngl_driver_load_texture (GskNglDriver *self,
int mag_filter)
{
GdkGLContext *context;
GdkTexture *downloaded_texture;
GdkTexture *downloaded_texture = NULL;
guint texture_id = 0;
GskNglTexture *t;
guint texture_id;
int height;
int width;
int format;
GskConversion conversion;
g_return_val_if_fail (GSK_IS_NGL_DRIVER (self), 0);
g_return_val_if_fail (GDK_IS_TEXTURE (texture), 0);
g_return_val_if_fail (GSK_IS_NGL_COMMAND_QUEUE (self->command_queue), 0);
context = self->command_queue->context;
width = gdk_texture_get_width (texture);
height = gdk_texture_get_height (texture);
format = gdk_texture_is_hdr (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))
{
@@ -764,31 +908,56 @@ gsk_ngl_driver_load_texture (GskNglDriver *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_texture_download_texture (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_texture_download_texture (texture);
profile = gdk_texture_get_color_profile (texture);
gl_texture_id = gdk_gl_texture_get_id (gl_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_ngl_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_ngl_texture_destroyed))
t->user = texture;
return t->texture_id;
}
}
}
downloaded_texture = gdk_texture_download_texture (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_ngl_command_queue_upload_texture (self->command_queue,
downloaded_texture,
0,
@@ -796,22 +965,30 @@ gsk_ngl_driver_load_texture (GskNglDriver *self,
width,
height,
min_filter,
mag_filter);
mag_filter,
&conversion);
t = gsk_ngl_texture_new (texture_id,
width, height, min_filter, mag_filter,
width, height, format, min_filter, mag_filter,
self->current_frame_id);
g_hash_table_insert (self->textures, GUINT_TO_POINTER (texture_id), t);
g_clear_object (&downloaded_texture);
if (conversion)
t = gsk_ngl_driver_convert_texture (self,
texture_id,
width, height, format,
min_filter, mag_filter,
conversion);
if (gdk_texture_set_render_data (texture, self, t, gsk_ngl_texture_destroyed))
t->user = texture;
gdk_gl_context_label_object_printf (context, GL_TEXTURE, t->texture_id,
"GdkTexture<%p> %d", texture, t->texture_id);
g_clear_object (&downloaded_texture);
return texture_id;
}
@@ -820,6 +997,7 @@ gsk_ngl_driver_load_texture (GskNglDriver *self,
* @self: a `GskNglDriver`
* @width: the width of the texture
* @height: the height of the texture
* @format: format for the texture. Should be GL_RGBA8, GL_RGBA16F or GL_RGBA32F
* @min_filter: GL_NEAREST or GL_LINEAR
* @mag_filter: GL_NEAREST or GL_FILTER
*
@@ -837,6 +1015,7 @@ GskNglTexture *
gsk_ngl_driver_create_texture (GskNglDriver *self,
float width,
float height,
int format,
int min_filter,
int mag_filter)
{
@@ -847,15 +1026,19 @@ gsk_ngl_driver_create_texture (GskNglDriver *self,
texture_id = gsk_ngl_command_queue_create_texture (self->command_queue,
width, height,
format,
min_filter, mag_filter);
texture = gsk_ngl_texture_new (texture_id,
width, height,
format,
min_filter, mag_filter,
self->current_frame_id);
g_hash_table_insert (self->textures,
GUINT_TO_POINTER (texture->texture_id),
texture);
texture->last_used_in_frame = self->current_frame_id;
return texture;
}
@@ -896,6 +1079,8 @@ gsk_ngl_driver_release_texture (GskNglDriver *self,
* @self: a `GskNglDriver`
* @width: the width for the render target
* @height: the height for the render target
* @format: the format to use. This should be GL_RGBA8,
* GL_RGBA16F or GL_RGBA32F
* @min_filter: the min filter to use for the texture
* @mag_filter: the mag filter to use for the texture
* @out_render_target: (out): a location for the render target
@@ -915,6 +1100,7 @@ gboolean
gsk_ngl_driver_create_render_target (GskNglDriver *self,
int width,
int height,
int format,
int min_filter,
int mag_filter,
GskNglRenderTarget **out_render_target)
@@ -947,6 +1133,7 @@ gsk_ngl_driver_create_render_target (GskNglDriver *self,
if (gsk_ngl_command_queue_create_render_target (self->command_queue,
width, height,
format,
min_filter, mag_filter,
&framebuffer_id, &texture_id))
{
@@ -955,6 +1142,7 @@ gsk_ngl_driver_create_render_target (GskNglDriver *self,
render_target = g_slice_new0 (GskNglRenderTarget);
render_target->min_filter = min_filter;
render_target->mag_filter = mag_filter;
render_target->format = format;
render_target->width = width;
render_target->height = height;
render_target->framebuffer_id = framebuffer_id;
@@ -1014,6 +1202,7 @@ gsk_ngl_driver_release_render_target (GskNglDriver *self,
texture = gsk_ngl_texture_new (render_target->texture_id,
render_target->width,
render_target->height,
render_target->format,
render_target->min_filter,
render_target->mag_filter,
self->current_frame_id);
@@ -1210,6 +1399,7 @@ gsk_ngl_driver_add_texture_slices (GskNglDriver *self,
int tex_width;
int tex_height;
int x = 0, y = 0;
int format;
g_assert (GSK_IS_NGL_DRIVER (self));
g_assert (GDK_IS_TEXTURE (texture));
@@ -1221,6 +1411,9 @@ gsk_ngl_driver_add_texture_slices (GskNglDriver *self,
tex_width = texture->width;
tex_height = texture->height;
format = gdk_texture_is_hdr (texture) ? GL_RGBA16F : GL_RGBA8;
cols = (texture->width / max_texture_size) + 1;
rows = (texture->height / max_texture_size) + 1;
@@ -1243,12 +1436,37 @@ gsk_ngl_driver_add_texture_slices (GskNglDriver *self,
int slice_height = MIN (max_texture_size, texture->height - y);
int slice_index = (col * rows) + row;
guint texture_id;
GskConversion conversion;
texture_id = gsk_ngl_command_queue_upload_texture (self->command_queue,
texture,
x, y,
slice_width, slice_height,
GL_NEAREST, GL_NEAREST);
GL_NEAREST, GL_NEAREST,
&conversion);
if (conversion)
{
t = gsk_ngl_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_ngl_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_ngl_texture_free (t);
}
slices[slice_index].rect.x = x;
slices[slice_index].rect.y = y;
@@ -1266,6 +1484,7 @@ gsk_ngl_driver_add_texture_slices (GskNglDriver *self,
/* Allocate one Texture for the entire thing. */
t = gsk_ngl_texture_new (0,
tex_width, tex_height,
GL_RGBA8,
GL_NEAREST, GL_NEAREST,
self->current_frame_id);

View File

@@ -88,6 +88,7 @@ struct _GskNglRenderTarget
guint texture_id;
int min_filter;
int mag_filter;
int format;
int width;
int height;
};
@@ -144,6 +145,7 @@ GdkGLContext *gsk_ngl_driver_get_context (GskNglDriver
gboolean gsk_ngl_driver_create_render_target (GskNglDriver *self,
int width,
int height,
int format,
int min_filter,
int mag_filter,
GskNglRenderTarget **render_target);
@@ -166,6 +168,7 @@ guint gsk_ngl_driver_load_texture (GskNglDriver
GskNglTexture *gsk_ngl_driver_create_texture (GskNglDriver *self,
float width,
float height,
int format,
int min_filter,
int mag_filter);
void gsk_ngl_driver_release_texture (GskNglDriver *self,

View File

@@ -21,13 +21,21 @@
#include "config.h"
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdkmemorytextureprivate.h>
#include <gdk/gdkmemoryformatprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdkcolorprofileprivate.h>
#include "gsknglcommandqueueprivate.h"
#include "gskngldriverprivate.h"
#include "gsknglglyphlibraryprivate.h"
#ifdef HAVE_PANGOFT
#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 (GskNglGlyphLibrary, gsk_ngl_glyph_library, GSK_TYPE_GL_TEXTURE_LIBRARY)
@@ -161,16 +169,26 @@ gsk_ngl_glyph_library_create_surface (GskNglGlyphLibrary *self,
static void
render_glyph (cairo_surface_t *surface,
const cairo_scaled_font_t *scaled_font,
cairo_scaled_font_t *scaled_font,
const GskNglGlyphKey *key,
const GskNglGlyphValue *value)
{
cairo_t *cr;
cairo_glyph_t glyph;
#ifdef HAVE_PANGOFT
FT_Face face;
FT_Bool darken = 1;
FT_Parameter property = { FT_PARAM_TAG_STEM_DARKENING, &darken };
#endif
g_assert (surface != NULL);
g_assert (scaled_font != NULL);
#ifdef HAVE_PANGOFT
face = cairo_ft_scaled_font_lock_face (scaled_font);
FT_Face_Properties (face, 1, &property);
#endif
cr = cairo_create (surface);
cairo_set_scaled_font (cr, scaled_font);
cairo_set_source_rgba (cr, 1, 1, 1, 1);
@@ -183,6 +201,10 @@ render_glyph (cairo_surface_t *surface,
cairo_destroy (cr);
cairo_surface_flush (surface);
#ifdef HAVE_PANGOFT
cairo_ft_scaled_font_unlock_face (scaled_font);
#endif
}
static void
@@ -237,10 +259,12 @@ gsk_ngl_glyph_library_upload_glyph (GskNglGlyphLibrary *self,
pixel_data = free_data = g_malloc (width * height * 4);
gdk_memory_convert (pixel_data,
width * 4,
GDK_MEMORY_CONVERT_GLES_RGBA,
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

@@ -21,9 +21,11 @@
#include "config.h"
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdkmemoryformatprivate.h>
#include <gdk/gdkmemorytextureprivate.h>
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdktextureprivate.h>
#include <gdk/gdkcolorprofileprivate.h>
#include "gsknglcommandqueueprivate.h"
#include "gskngldriverprivate.h"
@@ -71,25 +73,105 @@ gsk_ngl_icon_library_init (GskNglIconLibrary *self)
gsk_ngl_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;
GdkMemoryFormat format;
memtex = GDK_MEMORY_TEXTURE (gdk_texture_download_texture (texture));
format = gdk_memory_texture_get_format (memtex);
if (!gdk_memory_format_gl_format (format,
gdk_gl_context_get_use_es (context),
gl_internalformat,
gl_format,
gl_type))
{
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_convert (memtex,
format,
gdk_color_profile_get_srgb_linear (),
NULL);
}
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_ngl_icon_library_add (GskNglIconLibrary *self,
GdkTexture *key,
const GskNglIconData **out_value)
{
GskNglTextureLibrary *tl = (GskNglTextureLibrary *)self;
GdkGLContext *context = gdk_gl_context_get_current ();
G_GNUC_UNUSED gint64 start_time = GDK_PROFILER_CURRENT_TIME;
cairo_surface_t *surface;
GdkMemoryTexture *memtex;
GskNglIconData *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_NGL_ICON_LIBRARY (self));
g_assert (GDK_IS_TEXTURE (key));
@@ -106,104 +188,117 @@ gsk_ngl_icon_library_add (GskNglIconLibrary *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 (),
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_CONVERT_GLES_RGBA,
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_memory_texture_get_format (memtex);
bpp = gdk_memory_format_bytes_per_pixel (format);
texture_id = GSK_NGL_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_use_es (context) ||
gdk_gl_context_has_version (context, 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_use_es (context) ||
gdk_gl_context_has_version (context, 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 ());
*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_NGL_DEFINE_PROGRAM (unblurred_outset_shadow,
GSK_NGL_ADD_UNIFORM (1, UNBLURRED_OUTSET_SHADOW_SPREAD, u_spread)
GSK_NGL_ADD_UNIFORM (2, UNBLURRED_OUTSET_SHADOW_OFFSET, u_offset)
GSK_NGL_ADD_UNIFORM (3, UNBLURRED_OUTSET_SHADOW_OUTLINE_RECT, u_outline_rect))
GSK_NGL_DEFINE_PROGRAM (postprocessing,
"/org/gtk/libgsk/ngl/postprocessing.glsl",
GSK_NGL_NO_UNIFORMS)
GSK_NGL_DEFINE_PROGRAM (linearize,
"/org/gtk/libgsk/ngl/linearize.glsl",
GSK_NGL_NO_UNIFORMS)
GSK_NGL_DEFINE_PROGRAM (premultiply,
"/org/gtk/libgsk/ngl/premultiply.glsl",
GSK_NGL_NO_UNIFORMS)
GSK_NGL_DEFINE_PROGRAM (linearize_premultiply,
"/org/gtk/libgsk/ngl/linearize_premultiply.glsl",
GSK_NGL_NO_UNIFORMS)

View File

@@ -22,8 +22,11 @@
#include <gdk/gdkprofilerprivate.h>
#include <gdk/gdkdisplayprivate.h>
#include <gdk/gdkglcontextprivate.h>
#include <gdk/gdksurfaceprivate.h>
#include <gsk/gskdebugprivate.h>
#include <gsk/gskrendererprivate.h>
#include <gsk/gskrendernodeprivate.h>
#include "gsknglcommandqueueprivate.h"
#include "gskngldriverprivate.h"
@@ -245,6 +248,7 @@ gsk_ngl_renderer_render_texture (GskRenderer *renderer,
if (gsk_ngl_driver_create_render_target (self->driver,
width, height,
GL_RGBA8,
GL_NEAREST, GL_NEAREST,
&render_target))
{

View File

@@ -29,6 +29,8 @@
#include <gsk/gskrendernodeprivate.h>
#include <gsk/gskglshaderprivate.h>
#include <gdk/gdktextureprivate.h>
#include <gdk/gdkcolorprofileprivate.h>
#include <gdk/gdkmemoryformatprivate.h>
#include <gsk/gsktransformprivate.h>
#include <gsk/gskroundedrectprivate.h>
#include <math.h>
@@ -162,6 +164,11 @@ struct _GskNglRenderJob
/* If we should be rendering red zones over fallback nodes */
guint debug_fallback : 1;
/* Format of the framebuffer we are rendering on, used in
* determining the format for intermediate textures
*/
int framebuffer_format;
};
typedef struct _GskNglRenderOffscreen
@@ -198,6 +205,16 @@ static gboolean gsk_ngl_render_job_visit_node_with_offscreen (GskNglRenderJob
const GskRenderNode *node,
GskNglRenderOffscreen *offscreen);
static inline int
get_target_format (GskNglRenderJob *job,
const GskRenderNode *node)
{
if (!gsk_render_node_is_hdr (node))
return GL_RGBA8;
return job->framebuffer_format;
}
static inline void
init_full_texture_region (GskNglRenderOffscreen *offscreen)
{
@@ -924,11 +941,24 @@ gsk_ngl_render_job_update_clip (GskNglRenderJob *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_R32G32B32_FLOAT,
gdk_color_profile_get_srgb_linear (),
(guchar *) rgba,
sizeof (float) * 4,
GDK_MEMORY_R32G32B32_FLOAT,
gdk_color_profile_get_srgb (),
1,
1);
d.alpha = rgba->alpha;
float_to_half4 ((const float *)&d, h);
}
/* fill_vertex_data */
@@ -1162,6 +1192,9 @@ gsk_ngl_render_job_visit_as_fallback (GskNglRenderJob *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);
@@ -1258,6 +1291,7 @@ blur_offscreen (GskNglRenderJob *job,
if (!gsk_ngl_driver_create_render_target (job->driver,
MAX (texture_to_blur_width, 1),
MAX (texture_to_blur_height, 1),
job->framebuffer_format,
GL_NEAREST, GL_NEAREST,
&pass1))
return 0;
@@ -1268,6 +1302,7 @@ blur_offscreen (GskNglRenderJob *job,
if (!gsk_ngl_driver_create_render_target (job->driver,
texture_to_blur_width,
texture_to_blur_height,
job->framebuffer_format,
GL_NEAREST, GL_NEAREST,
&pass2))
return gsk_ngl_driver_release_render_target (job->driver, pass1, FALSE);
@@ -2179,6 +2214,7 @@ gsk_ngl_render_job_visit_blurred_inset_shadow_node (GskNglRenderJob *job,
if (!gsk_ngl_driver_create_render_target (job->driver,
texture_width, texture_height,
get_target_format (job, node),
GL_NEAREST, GL_NEAREST,
&render_target))
g_assert_not_reached ();
@@ -2449,6 +2485,7 @@ gsk_ngl_render_job_visit_blurred_outset_shadow_node (GskNglRenderJob *job,
gsk_ngl_driver_create_render_target (job->driver,
texture_width, texture_height,
get_target_format (job, node),
GL_NEAREST, GL_NEAREST,
&render_target);
@@ -3533,7 +3570,8 @@ gsk_ngl_render_job_visit_repeat_node (GskNglRenderJob *job,
/* If the size of the repeat node is smaller than the size of the
* child node, we don't repeat at all and can just draw that part
* of the child texture... */
* of the child texture...
*/
if (rect_contains_rect (child_bounds, &node->bounds))
{
gsk_ngl_render_job_visit_clipped_child (job, child, &node->bounds);
@@ -3856,6 +3894,7 @@ gsk_ngl_render_job_visit_node_with_offscreen (GskNglRenderJob *job,
if (!gsk_ngl_driver_create_render_target (job->driver,
scaled_width, scaled_height,
get_target_format (job, node),
filter, filter,
&render_target))
g_assert_not_reached ();
@@ -3954,6 +3993,7 @@ gsk_ngl_render_job_render_flipped (GskNglRenderJob *job,
if (!gsk_ngl_command_queue_create_render_target (job->command_queue,
MAX (1, job->viewport.size.width),
MAX (1, job->viewport.size.height),
job->framebuffer_format,
GL_NEAREST, GL_NEAREST,
&framebuffer_id, &texture_id))
return;
@@ -3988,6 +4028,21 @@ gsk_ngl_render_job_render_flipped (GskNglRenderJob *job,
glDeleteTextures (1, &texture_id);
}
static void
gsk_ngl_render_job_postprocess (GskNglRenderJob *job,
guint texture_id)
{
gsk_ngl_command_queue_bind_framebuffer (job->command_queue, job->framebuffer);
gsk_ngl_command_queue_clear (job->command_queue, 0, &job->viewport);
gsk_ngl_render_job_begin_draw (job, CHOOSE_PROGRAM (job, postprocessing));
gsk_ngl_program_set_uniform_texture (job->current_program,
UNIFORM_SHARED_SOURCE, 0,
GL_TEXTURE_2D, GL_TEXTURE0, texture_id);
gsk_ngl_render_job_draw_offscreen_rect (job, &job->viewport);
gsk_ngl_render_job_end_draw (job);
}
void
gsk_ngl_render_job_render (GskNglRenderJob *job,
GskRenderNode *root)
@@ -3995,6 +4050,8 @@ gsk_ngl_render_job_render (GskNglRenderJob *job,
G_GNUC_UNUSED gint64 start_time;
guint scale_factor;
guint surface_height;
GskNglRenderTarget *render_target;
guint texture_id;
g_return_if_fail (job != NULL);
g_return_if_fail (root != NULL);
@@ -4005,15 +4062,27 @@ gsk_ngl_render_job_render (GskNglRenderJob *job,
gsk_ngl_command_queue_make_current (job->command_queue);
gsk_ngl_driver_create_render_target (job->driver,
job->viewport.size.width,
job->viewport.size.height,
job->framebuffer_format,
GL_NEAREST,
GL_NEAREST,
&render_target);
/* 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_ngl_command_queue_bind_framebuffer (job->command_queue, job->framebuffer);
gsk_ngl_command_queue_bind_framebuffer (job->command_queue, render_target->framebuffer_id);
gsk_ngl_command_queue_clear (job->command_queue, 0, &job->viewport);
gsk_ngl_render_job_visit_node (job, root);
gdk_gl_context_pop_debug_group (job->command_queue->context);
texture_id = gsk_ngl_driver_release_render_target (job->driver, render_target, FALSE);
gsk_ngl_render_job_postprocess (job, texture_id);
gdk_profiler_add_mark (start_time, GDK_PROFILER_CURRENT_TIME-start_time, "Build GL command queue", "");
#if 0
@@ -4044,6 +4113,23 @@ gsk_ngl_render_job_set_debug_fallback (GskNglRenderJob *job,
job->debug_fallback = !!debug_fallback;
}
static int
get_framebuffer_format (guint framebuffer)
{
int type, size;
glBindFramebuffer (GL_FRAMEBUFFER, framebuffer);
glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE, &type);
glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, &size);
if (type == GL_FLOAT && size == 32)
return GL_RGBA32F;
else if (size > 8)
return GL_RGBA16F;
else
return GL_RGBA8;
}
GskNglRenderJob *
gsk_ngl_render_job_new (GskNglDriver *driver,
const graphene_rect_t *viewport,
@@ -4070,6 +4156,7 @@ gsk_ngl_render_job_new (GskNglDriver *driver,
job->scale_x = scale_factor;
job->scale_y = scale_factor;
job->viewport = *viewport;
job->framebuffer_format = get_framebuffer_format (framebuffer);
gsk_ngl_render_job_set_alpha (job, 1.0f);
gsk_ngl_render_job_set_projection_from_rect (job, viewport, NULL);

View File

@@ -59,6 +59,7 @@ GskNglTexture *
gsk_ngl_texture_new (guint texture_id,
int width,
int height,
int format,
int min_filter,
int mag_filter,
gint64 frame_id)
@@ -70,6 +71,7 @@ gsk_ngl_texture_new (guint texture_id,
texture->link.data = texture;
texture->min_filter = min_filter;
texture->mag_filter = mag_filter;
texture->format = format;
texture->width = width;
texture->height = height;
texture->last_used_in_frame = frame_id;

View File

@@ -228,7 +228,7 @@ gsk_ngl_texture_library_pack_one (GskNglTextureLibrary *self,
height = MIN (height, self->driver->command_queue->max_texture_size);
}
texture = gsk_ngl_driver_create_texture (self->driver, width, height, GL_LINEAR, GL_LINEAR);
texture = gsk_ngl_driver_create_texture (self->driver, width, height, GL_RGBA8, GL_LINEAR, GL_LINEAR);
texture->permanent = TRUE;
return texture;

View File

@@ -67,6 +67,7 @@ struct _GskNglTexture
int height;
int min_filter;
int mag_filter;
int format;
/* Set when used by an atlas so we don't drop the texture */
guint permanent : 1;
@@ -75,6 +76,7 @@ struct _GskNglTexture
GskNglTexture *gsk_ngl_texture_new (guint texture_id,
int width,
int height,
int format,
int min_filter,
int mag_filter,
gint64 frame_id);

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

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

@@ -335,7 +335,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.21'
wayland_req = '>= 1.16.91'
graphene_req = '>= 1.9.1'
@@ -379,6 +380,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

@@ -292,12 +292,14 @@ gtk_gst_sink_texture_from_buffer (GtkGstSink *self,
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')